0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package org.netbeans.modules.visualweb.text;
0042:
0043: import java.util.HashMap;
0044: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomPosition;
0045: import org.netbeans.modules.visualweb.designer.WebForm;
0046: import org.netbeans.modules.visualweb.text.actions.SelectLineAction;
0047: import java.awt.AWTEvent;
0048: import java.awt.Color;
0049: import java.awt.Cursor;
0050: import java.awt.Dimension;
0051: import java.awt.Graphics;
0052: import java.awt.Point;
0053: import java.awt.Rectangle;
0054: import java.awt.datatransfer.StringSelection;
0055: import java.awt.datatransfer.Transferable;
0056: import java.awt.event.KeyEvent;
0057: import java.awt.event.MouseEvent;
0058: import java.util.Enumeration;
0059: import java.util.Hashtable;
0060: import java.util.Map;
0061: import java.util.Vector;
0062: import java.util.logging.Logger;
0063:
0064: import javax.accessibility.Accessible;
0065: import javax.accessibility.AccessibleContext;
0066: import javax.swing.Action;
0067: import javax.swing.ActionMap;
0068: import javax.swing.InputMap;
0069: import javax.swing.JComponent;
0070: import javax.swing.JViewport;
0071: import javax.swing.KeyStroke;
0072: import javax.swing.Scrollable;
0073: import javax.swing.SwingConstants;
0074: import javax.swing.UIManager;
0075: import javax.swing.text.Keymap;
0076: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomRange;
0077:
0078: import org.netbeans.modules.visualweb.text.actions.BeginLineAction;
0079: import org.netbeans.modules.visualweb.text.actions.BeginWordAction;
0080: import org.netbeans.modules.visualweb.text.actions.DefaultKeyTypedAction;
0081: import org.netbeans.modules.visualweb.text.actions.DeleteNextCharAction;
0082: import org.netbeans.modules.visualweb.text.actions.DeletePrevCharAction;
0083: import org.netbeans.modules.visualweb.text.actions.EndLineAction;
0084: import org.netbeans.modules.visualweb.text.actions.EndWordAction;
0085: import org.netbeans.modules.visualweb.text.actions.NextVisualPositionAction;
0086: import org.netbeans.modules.visualweb.text.actions.SelectAllAction;
0087:
0088: /**
0089: * Base Rave page editor component. Based heavily on JTextComponent in
0090: * javax.swing.text. This is the superclass for DesignerPane which
0091: * handles most of the basic text editing (caret, selection, keyboard
0092: * handling). Conceivably the DesignerPane specific stuff could be
0093: * moved up into this class. (Earlier I was actually subclassing the
0094: * real JTextComponent in DesignerPane which is why I had this
0095: * separation).
0096: *
0097: * @todo Remove various unused methods, refactor
0098: * @todo Focus traversal keys (see Swing's JEditorPane) for tab/shifttab'ing
0099: * in and out of this component, etc.
0100: * @todo Input method text composition support (see Swing's JTextComponent)
0101: * @todo Implement Accessible (see Swing's JTextComponent)
0102: * @todo Complete the getActions call to include tons of actions
0103: * (see DefaultEditorKit)
0104: */
0105: public abstract class DesignerPaneBase extends JComponent implements
0106: Scrollable, Accessible {
0107: /**
0108: * Name of the Action for moving the caret logically forward one position.
0109: *
0110: * @see #getActions
0111: */
0112: public static final String forwardAction = "caret-forward";
0113:
0114: /**
0115: * Name of the Action for moving the caret logically backward one position.
0116: *
0117: * @see #getActions
0118: */
0119: public static final String backwardAction = "caret-backward";
0120:
0121: /**
0122: * Name of the Action for moving the caret logically upward one position.
0123: *
0124: * @see #getActions
0125: */
0126: public static final String upAction = "caret-up";
0127:
0128: /**
0129: * Name of the Action for moving the caret logically downward one position.
0130: *
0131: * @see #getActions
0132: */
0133: public static final String downAction = "caret-down";
0134:
0135: /**
0136: * Name of the Action for extending the selection by moving the caret logically forward one
0137: * position.
0138: *
0139: * @see #getActions
0140: */
0141: public static final String selectionForwardAction = "selection-forward";
0142:
0143: /**
0144: * Name of the Action for extending the selection by moving the caret logically backward one
0145: * position.
0146: *
0147: * @see #getActions
0148: */
0149: public static final String selectionBackwardAction = "selection-backward";
0150:
0151: /**
0152: * Name of the Action for moving the caret logically upward one position, extending the
0153: * selection.
0154: *
0155: * @see #getActions
0156: */
0157: public static final String selectionUpAction = "selection-up";
0158:
0159: /**
0160: * Name of the Action for moving the caret logically downward one position, extending the
0161: * selection.
0162: *
0163: * @see #getActions
0164: */
0165: public static final String selectionDownAction = "selection-down";
0166:
0167: /**
0168: * Name of the action that is executed by default if a <em>key typed event</em> is received
0169: * and there is no keymap entry.
0170: *
0171: * @see #getActions
0172: */
0173: public static final String defaultKeyTypedAction = "default-typed";
0174:
0175: /**
0176: * Name of the action to delete the character of content that precedes the current caret
0177: * position.
0178: *
0179: * @see #getActions
0180: */
0181: public static final String deletePrevCharAction = "delete-previous";
0182:
0183: /**
0184: * Name of the action to delete the character of content that
0185: * follows the current caret position.
0186: * @see #getActions
0187: */
0188: public static final String deleteNextCharAction = "delete-next";
0189:
0190: /**
0191: * Name of the Action for selecting a word around the caret.
0192: *
0193: * @see #getActions
0194: */
0195: public static final String selectWordAction = "select-word";
0196:
0197: /**
0198: * Name of the <code>Action</code> for moving the caret to the beginning of a word.
0199: *
0200: * @see #getActions
0201: */
0202: public static final String beginWordAction = "caret-begin-word";
0203:
0204: /**
0205: * Name of the Action for moving the caret to the end of a word.
0206: *
0207: * @see #getActions
0208: */
0209: public static final String endWordAction = "caret-end-word";
0210:
0211: /**
0212: * Name of the <code>Action</code> for moving the caret to the beginning of a word, extending
0213: * the selection.
0214: *
0215: * @see #getActions
0216: */
0217: public static final String selectionBeginWordAction = "selection-begin-word";
0218:
0219: /**
0220: * Name of the Action for moving the caret to the end of a word, extending the selection.
0221: *
0222: * @see #getActions
0223: */
0224: public static final String selectionEndWordAction = "selection-end-word";
0225:
0226: /**
0227: * Name of the Action for selecting a line around the caret.
0228: *
0229: * @see #getActions
0230: */
0231: public static final String selectLineAction = "select-line";
0232:
0233: /**
0234: * Name of the Action for selecting the entire document
0235: * @see #getActions
0236: */
0237: public static final String selectAllAction = "select-all";
0238:
0239: /**
0240: * Name of the <code>Action</code> for moving the caret to the beginning of a line, extending
0241: * the selection.
0242: *
0243: * @see #getActions
0244: */
0245: public static final String selectionBeginLineAction = "selection-begin-line";
0246:
0247: /**
0248: * Name of the <code>Action</code> for moving the caret to the end of a line, extending the
0249: * selection.
0250: *
0251: * @see #getActions
0252: */
0253: public static final String selectionEndLineAction = "selection-end-line";
0254:
0255: /**
0256: * Name of the <code>Action</code> for moving the caret to the beginning of a line.
0257: *
0258: * @see #getActions
0259: */
0260: public static final String beginLineAction = "caret-begin-line";
0261:
0262: /**
0263: * Name of the <code>Action</code> for moving the caret to the end of a line.
0264: *
0265: * @see #getActions
0266: */
0267: public static final String endLineAction = "caret-end-line";
0268:
0269: /**
0270: * The most recent JTextComponent that has/had focus. The text actions use this via the
0271: * <code>getFocusedComponent</code> method as a fallback case when a JTextComponent doesn't
0272: * have focus.
0273: */
0274: private static DesignerPaneBase focusedComponent;
0275: private static final Action[] defaultActions = {
0276: new DefaultKeyTypedAction(),
0277: new BeginWordAction(beginWordAction, false),
0278: new EndWordAction(endWordAction, false),
0279: new BeginWordAction(selectionBeginWordAction, true),
0280: new EndWordAction(selectionEndWordAction, true),
0281: new BeginLineAction(beginLineAction, false),
0282: new EndLineAction(endLineAction, false),
0283: new BeginLineAction(selectionBeginLineAction, true),
0284: new EndLineAction(selectionEndLineAction, true),
0285: new DeletePrevCharAction(),
0286: new DeleteNextCharAction(),
0287:
0288: /*
0289: * , new InsertContentAction(), new ReadOnlyAction(), new
0290: * WritableAction(), new CutAction(), new CopyAction(), new PasteAction(), new
0291: * VerticalPageAction(pageUpAction, -1, false), new VerticalPageAction(pageDownAction,
0292: * 1, false), new VerticalPageAction(selectionPageUpAction, -1, true), new
0293: * VerticalPageAction(selectionPageDownAction, 1, true), new
0294: * PageAction(selectionPageLeftAction, true, true), new
0295: * PageAction(selectionPageRightAction, false, true), new InsertBreakAction(), new
0296: * BeepAction(),
0297: */
0298: new NextVisualPositionAction(forwardAction, false,
0299: SwingConstants.EAST),
0300: new NextVisualPositionAction(backwardAction, false,
0301: SwingConstants.WEST),
0302: new NextVisualPositionAction(upAction, false,
0303: SwingConstants.NORTH),
0304: new NextVisualPositionAction(downAction, false,
0305: SwingConstants.SOUTH),
0306: new NextVisualPositionAction(selectionForwardAction, true,
0307: SwingConstants.EAST),
0308: new NextVisualPositionAction(selectionBackwardAction, true,
0309: SwingConstants.WEST),
0310: new NextVisualPositionAction(selectionUpAction, true,
0311: SwingConstants.NORTH),
0312: new NextVisualPositionAction(selectionDownAction, true,
0313: SwingConstants.SOUTH),
0314:
0315: /*
0316: * new PreviousWordAction(previousWordAction, false), new NextWordAction(nextWordAction, false),
0317: * new PreviousWordAction(selectionPreviousWordAction, true), new
0318: * NextWordAction(selectionNextWordAction, true), new BeginParagraphAction(beginParagraphAction,
0319: * false), new EndParagraphAction(endParagraphAction, false), new
0320: * BeginParagraphAction(selectionBeginParagraphAction, true), new
0321: * EndParagraphAction(selectionEndParagraphAction, true), new BeginAction(beginAction, false),
0322: * new EndAction(endAction, false), new BeginAction(selectionBeginAction, true), new
0323: * EndAction(selectionEndAction, true), new InsertTabAction(), new SelectWordAction(), */
0324: new SelectLineAction(), /*new SelectParagraphAction(), */
0325: new SelectAllAction(), /*new UnselectAction(),
0326: * new ToggleComponentOrientationAction(),
0327: */
0328: /*
0329: * From HTMLEditorKit new DumpModelAction()
0330: *
0331: * new InsertHTMLTextAction("InsertTable", INSERT_TABLE_HTML, HTML.Tag.BODY, HTML.Tag.TABLE),
0332: * new InsertHTMLTextAction("InsertTableRow", INSERT_TABLE_HTML, HTML.Tag.TABLE, HTML.Tag.TR,
0333: * HTML.Tag.BODY, HTML.Tag.TABLE), new InsertHTMLTextAction("InsertTableDataCell",
0334: * INSERT_TABLE_HTML, HTML.Tag.TR, HTML.Tag.TD, HTML.Tag.BODY, HTML.Tag.TABLE), new
0335: * InsertHTMLTextAction("InsertUnorderedList", INSERT_UL_HTML, HTML.Tag.BODY, HTML.Tag.UL), new
0336: * InsertHTMLTextAction("InsertUnorderedListItem", INSERT_UL_HTML, HTML.Tag.UL, HTML.Tag.LI,
0337: * HTML.Tag.BODY, HTML.Tag.UL), new InsertHTMLTextAction("InsertOrderedList", INSERT_OL_HTML,
0338: * HTML.Tag.BODY, HTML.Tag.OL), new InsertHTMLTextAction("InsertOrderedListItem",
0339: * INSERT_OL_HTML, HTML.Tag.OL, HTML.Tag.LI, HTML.Tag.BODY, HTML.Tag.OL), new InsertHRAction(),
0340: * new InsertHTMLTextAction("InsertPre", INSERT_PRE_HTML, HTML.Tag.BODY, HTML.Tag.PRE),
0341: * nextLinkAction, previousLinkAction, activateLinkAction
0342: */
0343: };
0344:
0345: private static Map<String, Keymap> keymapTable = null;
0346:
0347: /**
0348: * The default keymap that will be shared by all <code>JTextComponent</code> instances unless
0349: * they have had a different keymap set.
0350: */
0351: public static final String DEFAULT_KEYMAP = "default";
0352:
0353: static {
0354: try {
0355: keymapTable = new Hashtable<String, Keymap>(17);
0356:
0357: Keymap binding = addKeymap(DEFAULT_KEYMAP, null);
0358: binding.setDefaultAction(new DefaultKeyTypedAction());
0359: } catch (Throwable e) {
0360: e.printStackTrace();
0361: }
0362: }
0363:
0364: /**
0365: * @see #getUIClassID
0366: * @see #readObject
0367: */
0368: private static final String uiClassID = "EditorPaneUI";
0369: // private Document model = null;
0370: // private final Document model;
0371:
0372: // --- member variables ----------------------------------
0373:
0374: /**
0375: * The caret used to display the insert position and navigate throughout the document.
0376: *
0377: * PENDING(prinz) This should be serializable, default installed by UI.
0378: */
0379: private transient DesignerCaret caret;
0380:
0381: /**
0382: * The current key bindings in effect.
0383: *
0384: * PENDING(prinz) This should be serializable, default installed by UI.
0385: */
0386: private transient Keymap keymap;
0387: private Color caretColor;
0388: private Color selectionColor;
0389: private Color selectedTextColor;
0390:
0391: /**
0392: * Creates a new <code>JTextComponent</code>. Listeners for caret events are established, and
0393: * the pluggable UI installed. The component is marked as editable. No layout manager is used,
0394: * because layout is managed by the view subsystem of text. The document model is set to
0395: * <code>null</code>.
0396: */
0397: public DesignerPaneBase(/*Document document*/) {
0398: super ();
0399: // this.model = document;
0400:
0401: // enable InputMethodEvent for on-the-spot pre-editing
0402: enableEvents(AWTEvent.KEY_EVENT_MASK
0403: | AWTEvent.INPUT_METHOD_EVENT_MASK);
0404: setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
0405: setLayout(null); // layout is managed by the box hierarchy
0406:
0407: // TODO: find out if I still need to defer this!
0408: // Necessary, because updateUI causes a huge cascade - down
0409: // to View creation etc. Some of the Subviews (FormView in
0410: // particular) consults the container for its page reference;
0411: // but our child classes' constructors (such as the one in
0412: // DesignerPane) has not yet had a chance to complete. So the
0413: // child will do it instead for now. HACK HACK HACK YUCK YUCK YUCK.
0414: //updateUI();
0415: }
0416:
0417: // XXX Temp only.
0418: public abstract WebForm getWebForm();
0419:
0420: /**
0421: * Fetches the user-interface factory for this text-oriented editor.
0422: *
0423: * @return the factory
0424: */
0425: public DesignerPaneBaseUI getUI() {
0426: return (DesignerPaneBaseUI) ui;
0427: }
0428:
0429: /**
0430: * Sets the user-interface factory for this text-oriented editor.
0431: *
0432: * @param ui
0433: * the factory
0434: */
0435: public void setUI(DesignerPaneBaseUI ui) {
0436: super .setUI(ui);
0437: }
0438:
0439: /**
0440: * Reloads the pluggable UI. The key used to fetch the new interface is
0441: * <code>getUIClassID()</code>. The type of the UI is <code>DesignerPaneBaseUI</code>.
0442: * <code>invalidate</code> is called after setting the UI.
0443: */
0444: public void updateUI() {
0445: info("Updating UI, ui=" + ui); // TEMP
0446: setUI((DesignerPaneBaseUI) UIManager.getUI(this ));
0447: info("after update, ui=" + ui); // TEMP
0448: invalidate();
0449: }
0450:
0451: /**
0452: * Returns the JTextComponent that most recently had focus. The returned value may currently
0453: * have focus.
0454: */
0455: public static final DesignerPaneBase getFocusedComponent() {
0456: return focusedComponent;
0457: }
0458:
0459: // /**
0460: // * Fetches the model associated with the editor. This is primarily for the UI to get at the
0461: // * minimal amount of state required to be a text editor. Subclasses will return the actual type
0462: // * of the model which will typically be something that extends Document.
0463: // *
0464: // * @return the model
0465: // */
0466: // public Document getDocument() {
0467: // return model;
0468: // }
0469:
0470: /**
0471: * Fetches the command list for the editor. This is the list of commands supported by the
0472: * plugged-in UI augmented by the collection of commands that the editor itself supports. These
0473: * are useful for binding to events, such as in a keymap.
0474: *
0475: * @return the command list
0476: */
0477: public Action[] getActions() {
0478: return defaultActions.clone();
0479: }
0480:
0481: /**
0482: * Fetches the caret that allows text-oriented navigation over the view.
0483: *
0484: * @return the caret
0485: */
0486: private/*public*/DesignerCaret getCaret() {
0487: return caret;
0488: }
0489:
0490: /**
0491: * Sets the caret to be used. By default this will be set
0492: * by the UI that gets installed. This can be changed to
0493: * a custom caret if desired. Setting the caret results in a
0494: * PropertyChange event ("caret") being fired.
0495: *
0496: * @param c the caret
0497: * @see #getCaret
0498: * @beaninfo
0499: * description: the caret used to select/navigate
0500: * bound: true
0501: * expert: true
0502: */
0503: public void setCaret(DesignerCaret c) {
0504: if (caret != null) {
0505: caret.deinstall(this );
0506: }
0507:
0508: DesignerCaret old = caret;
0509: caret = c;
0510:
0511: if (caret != null) {
0512: caret.install(this );
0513: }
0514:
0515: firePropertyChange("caret", old, caret);
0516: }
0517:
0518: /**
0519: * Sets the keymap to use for binding events to
0520: * actions. Setting to <code>null</code> effectively disables
0521: * keyboard input.
0522: * A PropertyChange event ("keymap") is fired when a new keymap
0523: * is installed.
0524: *
0525: * @param map the keymap
0526: * @see #getKeymap
0527: * @beaninfo
0528: * description: set of key event to action bindings to use
0529: * bound: true
0530: */
0531: public void setKeymap(Keymap map) {
0532: Keymap old = keymap;
0533: keymap = map;
0534: firePropertyChange("keymap", old, keymap);
0535: updateInputMap(old, map);
0536: }
0537:
0538: /**
0539: * Updates the <code>InputMap</code> s in response to a <code>Keymap</code> change.
0540: *
0541: * @param oldKm
0542: * the old <code>Keymap</code>
0543: * @param newKm
0544: * the new <code>Keymap</code>
0545: */
0546: void updateInputMap(Keymap oldKm, Keymap newKm) {
0547: // Locate the current KeymapWrapper.
0548: InputMap km = getInputMap(JComponent.WHEN_FOCUSED);
0549: InputMap last = km;
0550:
0551: while ((km != null) && !(km instanceof KeymapWrapper)) {
0552: last = km;
0553: km = km.getParent();
0554: }
0555:
0556: if (km != null) {
0557: // Found it, tweak the InputMap that points to it, as well
0558: // as anything it points to.
0559: if (newKm == null) {
0560: if (last != km) {
0561: last.setParent(km.getParent());
0562: } else {
0563: last.setParent(null);
0564: }
0565: } else {
0566: InputMap newKM = new KeymapWrapper(newKm);
0567: last.setParent(newKM);
0568:
0569: if (last != km) {
0570: newKM.setParent(km.getParent());
0571: }
0572: }
0573: } else if (newKm != null) {
0574: km = getInputMap(JComponent.WHEN_FOCUSED);
0575:
0576: if (km != null) {
0577: // Couldn't find it.
0578: // Set the parent of WHEN_FOCUSED InputMap to be the new one.
0579: InputMap newKM = new KeymapWrapper(newKm);
0580: newKM.setParent(km.getParent());
0581: km.setParent(newKM);
0582: }
0583: }
0584:
0585: // Do the same thing with the ActionMap
0586: ActionMap am = getActionMap();
0587: ActionMap lastAM = am;
0588:
0589: while ((am != null) && !(am instanceof KeymapActionMap)) {
0590: lastAM = am;
0591: am = am.getParent();
0592: }
0593:
0594: if (am != null) {
0595: // Found it, tweak the Actionap that points to it, as well
0596: // as anything it points to.
0597: if (newKm == null) {
0598: if (lastAM != am) {
0599: lastAM.setParent(am.getParent());
0600: } else {
0601: lastAM.setParent(null);
0602: }
0603: } else {
0604: ActionMap newAM = new KeymapActionMap(newKm);
0605: lastAM.setParent(newAM);
0606:
0607: if (lastAM != am) {
0608: newAM.setParent(am.getParent());
0609: }
0610: }
0611: } else if (newKm != null) {
0612: am = getActionMap();
0613:
0614: if (am != null) {
0615: // Couldn't find it.
0616: // Set the parent of ActionMap to be the new one.
0617: ActionMap newAM = new KeymapActionMap(newKm);
0618: newAM.setParent(am.getParent());
0619: am.setParent(newAM);
0620: }
0621: }
0622: }
0623:
0624: /**
0625: * Fetches the keymap currently active in this text component.
0626: *
0627: * @return the keymap
0628: */
0629: public Keymap getKeymap() {
0630: return keymap;
0631: }
0632:
0633: /**
0634: * Adds a new keymap into the keymap hierarchy. Keymap bindings resolve from bottom up so an
0635: * attribute specified in a child will override an attribute specified in the parent.
0636: *
0637: * @param nm
0638: * the name of the keymap (must be unique within the collection of named keymaps in
0639: * the document); the name may be <code>null</code> if the keymap is unnamed, but
0640: * the caller is responsible for managing the reference returned as an unnamed keymap
0641: * can't be fetched by name
0642: * @param parent
0643: * the parent keymap; this may be <code>null</code> if unspecified bindings need
0644: * not be resolved in some other keymap
0645: * @return the keymap
0646: */
0647: public static Keymap addKeymap(String nm, Keymap parent) {
0648: Keymap map = new DefaultKeymap(nm, parent);
0649:
0650: if (nm != null) {
0651: // add a named keymap, a class of bindings
0652: keymapTable.put(nm, map);
0653: }
0654:
0655: return map;
0656: }
0657:
0658: /**
0659: * Removes a named keymap previously added to the document. Keymaps with <code>null</code>
0660: * names may not be removed in this way.
0661: *
0662: * @param nm
0663: * the name of the keymap to remove
0664: * @return the keymap that was removed
0665: */
0666: public static Keymap removeKeymap(String nm) {
0667: return keymapTable.remove(nm);
0668: }
0669:
0670: /**
0671: * Fetches a named keymap previously added to the document. This does not work with
0672: * <code>null</code> -named keymaps.
0673: *
0674: * @param nm
0675: * the name of the keymap
0676: * @return the keymap
0677: */
0678: public static Keymap getKeymap(String nm) {
0679: return keymapTable.get(nm);
0680: }
0681:
0682: /**
0683: * <p>
0684: * Loads a keymap with a bunch of bindings. This can be used to take a static table of
0685: * definitions and load them into some keymap. The following example illustrates an example of
0686: * binding some keys to the cut, copy, and paste actions associated with a JTextComponent. A
0687: * code fragment to accomplish this might look as follows:
0688: *
0689: * <pre><code>
0690: * static final JTextComponent.KeyBinding[] defaultBindings = {
0691: * new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK),
0692: * DefaultEditorKit.copyAction),
0693: * new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK),
0694: * DefaultEditorKit.pasteAction),
0695: * new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK),
0696: * DefaultEditorKit.cutAction),};
0697: * JTextComponent c = new JTextPane();
0698: * Keymap k = c.getKeymap();
0699: * JTextComponent.loadKeymap(k, defaultBindings, c.getActions());
0700: * </code></pre>
0701: *
0702: * The sets of bindings and actions may be empty but must be non- <code>null</code>.
0703: *
0704: * @param map
0705: * the keymap
0706: * @param bindings
0707: * the bindings
0708: * @param actions
0709: * the set of actions
0710: */
0711: public static void loadKeymap(Keymap map, KeyBinding[] bindings,
0712: Action[] actions) {
0713: Map<String, Action> h = new HashMap<String, Action>();
0714:
0715: for (int i = 0; i < actions.length; i++) {
0716: Action a = actions[i];
0717: String value = (String) a.getValue(Action.NAME);
0718: h.put(((value != null) ? value : ""), a);
0719: }
0720:
0721: for (int i = 0; i < bindings.length; i++) {
0722: Action a = h.get(bindings[i].actionName);
0723:
0724: if (a != null) {
0725: map.addActionForKeyStroke(bindings[i].key, a);
0726: }
0727: }
0728: }
0729:
0730: /**
0731: * Fetches the current color used to render the caret.
0732: *
0733: * @return the color
0734: */
0735: public Color getCaretColor() {
0736: return caretColor;
0737: }
0738:
0739: /**
0740: * Sets the current color used to render the caret.
0741: * Setting to <code>null</code> effectively restores the default color.
0742: * Setting the color results in a PropertyChange event ("caretColor")
0743: * being fired.
0744: *
0745: * @param c the color
0746: * @see #getCaretColor
0747: * @beaninfo
0748: * description: the color used to render the caret
0749: * bound: true
0750: * preferred: true
0751: */
0752: public void setCaretColor(Color c) {
0753: Color old = caretColor;
0754: caretColor = c;
0755: firePropertyChange("caretColor", old, caretColor);
0756: }
0757:
0758: /**
0759: * Fetches the current color used to render the selection.
0760: *
0761: * @return the color
0762: */
0763: public Color getSelectionColor() {
0764: return selectionColor;
0765: }
0766:
0767: /**
0768: * Sets the current color used to render the selection.
0769: * Setting the color to <code>null</code> is the same as setting
0770: * <code>Color.white</code>. Setting the color results in a
0771: * PropertyChange event ("selectionColor").
0772: *
0773: * @param c the color
0774: * @see #getSelectionColor
0775: * @beaninfo
0776: * description: color used to render selection background
0777: * bound: true
0778: * preferred: true
0779: */
0780: public void setSelectionColor(Color c) {
0781: Color old = selectionColor;
0782: selectionColor = c;
0783: firePropertyChange("selectionColor", old, selectionColor);
0784: }
0785:
0786: /**
0787: * Fetches the current color used to render the selected text.
0788: *
0789: * @return the color
0790: */
0791: public Color getSelectedTextColor() {
0792: return selectedTextColor;
0793: }
0794:
0795: /**
0796: * Sets the current color used to render the selected text.
0797: * Setting the color to <code>null</code> is the same as
0798: * <code>Color.black</code>. Setting the color results in a
0799: * PropertyChange event ("selectedTextColor") being fired.
0800: *
0801: * @param c the color
0802: * @see #getSelectedTextColor
0803: * @beaninfo
0804: * description: color used to render selected text
0805: * bound: true
0806: * preferred: true
0807: */
0808: public void setSelectedTextColor(Color c) {
0809: Color old = selectedTextColor;
0810: selectedTextColor = c;
0811: firePropertyChange("selectedTextColor", old, selectedTextColor);
0812: }
0813:
0814: /**
0815: * Fetches a portion of the text represented by the component. Returns an empty string if length
0816: * is 0.
0817: *
0818: * @param offs
0819: * the offset >= 0
0820: * @param len
0821: * the length >= 0
0822: * @return the text
0823: * @exception BadLocationException
0824: * if the offset or length are invalid
0825: */
0826: // public String getText(Position offs, int len) {
0827: public String getText(DomPosition offs, int len) {
0828: throw new RuntimeException("Not yet implemented!"); // XXX
0829:
0830: //return "";
0831: }
0832:
0833: // /**
0834: // * Converts the given location in the model to a place in the view coordinate system. The
0835: // * component must have a positive size for this translation to be computed (i.e. layout cannot
0836: // * be computed until the component has been sized). The component does not have to be visible or
0837: // * painted.
0838: // *
0839: // * @param pos
0840: // * the position >= 0
0841: // * @return the coordinates as a rectangle, with (r.x, r.y) as the location in the coordinate
0842: // * system, or null if the component does not yet have a positive size.
0843: // * @exception BadLocationException
0844: // * if the given position does not represent a valid location in the associated
0845: // * document
0846: // * @see DesignerPaneBaseUI#modelToView
0847: // */
0848: // public Rectangle modelToView(Position pos) {
0849: // return getUI().modelToView(/*this,*/ pos);
0850: // }
0851:
0852: // /**
0853: // * Converts the given place in the view coordinate system to the nearest representative location
0854: // * in the model. The component must have a positive size for this translation to be computed
0855: // * (i.e. layout cannot be computed until the component has been sized). The component does not
0856: // * have to be visible or painted.
0857: // *
0858: // * @param pt
0859: // * the location in the view to translate
0860: // * @return the offset >= 0 from the start of the document, or -1 if the component does not yet
0861: // * have a positive size.
0862: // * @see DesignerPaneBaseUI#viewToModel
0863: // */
0864: // public Position viewToModel(Point pt) {
0865: // return getUI().viewToModel(this, pt);
0866: // }
0867:
0868: // /**
0869: // * Moves the caret to a new position, leaving behind a mark defined by the last time
0870: // * <code>setCaretPosition</code> was called. This forms a selection. If the document is
0871: // * <code>null</code>, does nothing. The position must be between 0 and the length of the
0872: // * component's text or else an exception is thrown.
0873: // *
0874: // * @param pos
0875: // * the position
0876: // * @exception IllegalArgumentException
0877: // * if the value supplied for <code>position</code> is less than zero or greater
0878: // * than the component's text length
0879: // * @see #setCaretPosition
0880: // */
0881: //// public void moveCaretPosition(Position pos) {
0882: // public void moveCaretPosition(DomPosition pos) {
0883: // if (caret != null) {
0884: // caret.moveDot(pos);
0885: // }
0886: // }
0887: //
0888: // /**
0889: // * Sets the position of the text insertion caret for the
0890: // * <code>TextComponent</code>. Note that the caret tracks change,
0891: // * so this may move if the underlying text of the component is changed.
0892: // * If the document is <code>null</code>, does nothing. The position
0893: // * must be between 0 and the length of the component's text or else
0894: // * an exception is thrown.
0895: // *
0896: // * @param position the position
0897: // * @exception IllegalArgumentException if the value supplied
0898: // * for <code>position</code> is less than zero or greater
0899: // * than the component's text length
0900: // * @beaninfo
0901: // * description: the caret position
0902: // */
0903: //// public void setCaretPosition(Position position) {
0904: // public void setCaretPosition(DomPosition position) {
0905: // if (caret != null) {
0906: // caret.setDot(position);
0907: // }
0908: // }
0909:
0910: /**
0911: * Returns the position of the text insertion caret for the text component.
0912: *
0913: * @return the position of the text insertion caret for the text component >= 0
0914: */
0915: // public Position getCaretPosition() {
0916: public DomPosition getCaretPosition() {
0917: if (caret == null) {
0918: // return Position.NONE;
0919: return DomPosition.NONE;
0920: }
0921:
0922: return caret.getDot();
0923: }
0924:
0925: /**
0926: * Selects the text between the specified start and end positions.
0927: * <p>
0928: * This method sets the start and end positions of the selected text, enforcing the restriction
0929: * that the start position must be greater than or equal to zero. The end position must be
0930: * greater than or equal to the start position, and less than or equal to the length of the text
0931: * component's text.
0932: * <p>
0933: * If the caller supplies values that are inconsistent or out of bounds, the method enforces
0934: * these constraints silently, and without failure. Specifically, if the start position or end
0935: * position is greater than the length of the text, it is reset to equal the text length. If the
0936: * start position is less than zero, it is reset to zero, and if the end position is less than
0937: * the start position, it is reset to the start position.
0938: * <p>
0939: * This call is provided for backward compatibility. It is routed to a call to
0940: * <code>setCaretPosition</code> followed by a call to <code>moveCaretPosition</code>. The
0941: * preferred way to manage selection is by calling those methods directly.
0942: *
0943: * @param selectionStart
0944: * the start position of the text
0945: * @param selectionEnd
0946: * the end position of the text
0947: * @see #setCaretPosition
0948: * @see #moveCaretPosition
0949: */
0950: // public void select(Position selectionStart, Position selectionEnd) {
0951: public void select(DomPosition selectionStart,
0952: DomPosition selectionEnd) {
0953: // argument adjustment done by java.awt.TextComponent
0954: // setCaretPosition(selectionStart);
0955: // moveCaretPosition(selectionEnd);
0956: setCaretDot(selectionStart);
0957: moveCaretDot(selectionEnd);
0958: }
0959:
0960: // --- Scrollable methods ---------------------------------------------
0961:
0962: /**
0963: * Returns the preferred size of the viewport for a view component. This is implemented to do
0964: * the default behavior of returning the preferred size of the component.
0965: *
0966: * @return the <code>preferredSize</code> of a <code>JViewport</code> whose view is this
0967: * <code>Scrollable</code>
0968: */
0969: public Dimension getPreferredScrollableViewportSize() {
0970: return getPreferredSize();
0971: }
0972:
0973: /**
0974: * Components that display logical rows or columns should compute the scroll increment that will
0975: * completely expose one new row or column, depending on the value of orientation. Ideally,
0976: * components should handle a partially exposed row or column by returning the distance required
0977: * to completely expose the item.
0978: * <p>
0979: * The default implementation of this is to simply return 10% of the visible area. Subclasses
0980: * are likely to be able to provide a much more reasonable value.
0981: *
0982: * @param visibleRect
0983: * the view area visible within the viewport
0984: * @param orientation
0985: * either <code>SwingConstants.VERTICAL</code> or
0986: * <code>SwingConstants.HORIZONTAL</code>
0987: * @param direction
0988: * less than zero to scroll up/left, greater than zero for down/right
0989: * @return the "unit" increment for scrolling in the specified direction
0990: * @exception IllegalArgumentException
0991: * for an invalid orientation
0992: * @see JScrollBar#setUnitIncrement
0993: */
0994: public int getScrollableUnitIncrement(Rectangle visibleRect,
0995: int orientation, int direction) {
0996: switch (orientation) {
0997: case SwingConstants.VERTICAL:
0998: return visibleRect.height / 10;
0999:
1000: case SwingConstants.HORIZONTAL:
1001: return visibleRect.width / 10;
1002:
1003: default:
1004: throw new IllegalArgumentException("Invalid orientation: "
1005: + orientation);
1006: }
1007: }
1008:
1009: /**
1010: * Components that display logical rows or columns should compute the scroll increment that will
1011: * completely expose one block of rows or columns, depending on the value of orientation.
1012: * <p>
1013: * The default implementation of this is to simply return the visible area. Subclasses will
1014: * likely be able to provide a much more reasonable value.
1015: *
1016: * @param visibleRect
1017: * the view area visible within the viewport
1018: * @param orientation
1019: * either <code>SwingConstants.VERTICAL</code> or
1020: * <code>SwingConstants.HORIZONTAL</code>
1021: * @param direction
1022: * less than zero to scroll up/left, greater than zero for down/right
1023: * @return the "block" increment for scrolling in the specified direction
1024: * @exception IllegalArgumentException
1025: * for an invalid orientation
1026: * @see JScrollBar#setBlockIncrement
1027: */
1028: public int getScrollableBlockIncrement(Rectangle visibleRect,
1029: int orientation, int direction) {
1030: switch (orientation) {
1031: case SwingConstants.VERTICAL:
1032: return visibleRect.height;
1033:
1034: case SwingConstants.HORIZONTAL:
1035: return visibleRect.width;
1036:
1037: default:
1038: throw new IllegalArgumentException("Invalid orientation: "
1039: + orientation);
1040: }
1041: }
1042:
1043: /////////////////
1044: // Accessibility support
1045: ////////////////
1046:
1047: /**
1048: * Gets the <code>AccessibleContext</code> associated with this <code>DesignerPaneBase</code>.
1049: * @return an <code>AccessibleDesignerPaneBase</code> that serves as the
1050: * <code>AccessibleContext</code> of this <code>DesignerPaneBase</code>
1051: */
1052: public AccessibleContext getAccessibleContext() {
1053: if (accessibleContext == null) {
1054: accessibleContext = new AccessibleDesignerPaneBase();
1055: }
1056:
1057: return accessibleContext;
1058: }
1059:
1060: /**
1061: * Gets the class ID for the UI.
1062: *
1063: * @return the string "EditorPaneUI"
1064: * @see JComponent#getUIClassID
1065: * @see UIDefaults#getUI
1066: */
1067: public String getUIClassID() {
1068: return uiClassID;
1069: }
1070:
1071: // --- java.awt.Component methods --------------------------
1072:
1073: /**
1074: * Returns the preferred size for the <code>JEditorPane</code>. The preferred size for
1075: * <code>JEditorPane</code> is slightly altered from the preferred size of the superclass. If
1076: * the size of the viewport has become smaller than the minimum size of the component, the
1077: * scrollable definition for tracking width or height will turn to false. The default viewport
1078: * layout will give the preferred size, and that is not desired in the case where the scrollable
1079: * is tracking. In that case the <em>normal</em> preferred size is adjusted to the minimum
1080: * size. This allows things like HTML tables to shrink down to their minimum size and then be
1081: * laid out at their minimum size, refusing to shrink any further.
1082: *
1083: * @return a <code>Dimension</code> containing the preferred size
1084: */
1085: public Dimension getPreferredSize() {
1086: Dimension d = super .getPreferredSize();
1087:
1088: if (getParent() instanceof JViewport) {
1089: JViewport port = (JViewport) getParent();
1090: DesignerPaneBaseUI ui = getUI();
1091: int prefWidth = d.width;
1092: int prefHeight = d.height;
1093:
1094: if (!getScrollableTracksViewportWidth()) {
1095: int w = port.getWidth();
1096: Dimension min = ui.getMinimumSize(this );
1097:
1098: if ((w != 0) && (w < min.width)) {
1099: // Only adjust to min if we have a valid size
1100: prefWidth = min.width;
1101: }
1102: }
1103:
1104: if (!getScrollableTracksViewportHeight()) {
1105: int h = port.getHeight();
1106: Dimension min = ui.getMinimumSize(this );
1107:
1108: if ((h != 0) && (h < min.height)) {
1109: // Only adjust to min if we have a valid size
1110: prefHeight = min.height;
1111: }
1112: }
1113:
1114: if ((prefWidth != d.width) || (prefHeight != d.height)) {
1115: d = new Dimension(prefWidth, prefHeight);
1116: }
1117: }
1118:
1119: return d;
1120: }
1121:
1122: // --- Scrollable ----------------------------------------
1123:
1124: /**
1125: * Returns true if a viewport should always force the width of this <code>Scrollable</code> to
1126: * match the width of the viewport.
1127: *
1128: * @return true if a viewport should force the Scrollables width to match its own, false
1129: * otherwise
1130: */
1131: public boolean getScrollableTracksViewportWidth() {
1132: if (getParent() instanceof JViewport) {
1133: JViewport port = (JViewport) getParent();
1134: DesignerPaneBaseUI ui = getUI();
1135: int w = port.getWidth();
1136: Dimension min = ui.getMinimumSize(this );
1137: Dimension max = ui.getMaximumSize(this );
1138:
1139: if ((w >= min.width) && (w <= max.width)) {
1140: return true;
1141: }
1142: }
1143:
1144: return false;
1145: }
1146:
1147: /**
1148: * Returns true if a viewport should always force the height of this <code>Scrollable</code>
1149: * to match the height of the viewport.
1150: *
1151: * @return true if a viewport should force the <code>Scrollable</code>'s height to match its
1152: * own, false otherwise
1153: */
1154: public boolean getScrollableTracksViewportHeight() {
1155: if (getParent() instanceof JViewport) {
1156: JViewport port = (JViewport) getParent();
1157: DesignerPaneBaseUI ui = getUI();
1158: int h = port.getHeight();
1159: Dimension min = ui.getMinimumSize(this );
1160:
1161: if (h >= min.height) {
1162: Dimension max = ui.getMaximumSize(this );
1163:
1164: if (h <= max.height) {
1165: return true;
1166: }
1167: }
1168: }
1169:
1170: return false;
1171: }
1172:
1173: /**
1174: * Binding record for creating key bindings.
1175: * <p>
1176: * <strong>Warning: </strong> Serialized objects of this class will not be compatible with
1177: * future Swing releases. The current serialization support is appropriate for short term
1178: * storage or RMI between applications running the same version of Swing. As of 1.4, support for
1179: * long term storage of all JavaBeans <sup><font size="-2">TM </font> </sup> has been added to
1180: * the <code>java.beans</code> package. Please see {@link java.beans.XMLEncoder}.
1181: */
1182: public static class KeyBinding {
1183: /**
1184: * The key.
1185: */
1186: public KeyStroke key;
1187:
1188: /**
1189: * The name of the action for the key.
1190: */
1191: public String actionName;
1192:
1193: /**
1194: * Creates a new key binding.
1195: *
1196: * @param key
1197: * the key
1198: * @param actionName
1199: * the name of the action for the key
1200: */
1201: public KeyBinding(KeyStroke key, String actionName) {
1202: this .key = key;
1203: this .actionName = actionName;
1204: }
1205: }
1206:
1207: static class DefaultKeymap implements Keymap {
1208: String nm;
1209: javax.swing.text.Keymap parent;
1210: Hashtable<KeyStroke, Action> bindings;
1211: Action defaultAction;
1212:
1213: DefaultKeymap(String nm, Keymap parent) {
1214: this .nm = nm;
1215: this .parent = parent;
1216: bindings = new Hashtable<KeyStroke, Action>();
1217: }
1218:
1219: /**
1220: * Fetch the default action to fire if a key is typed (ie a KEY_TYPED KeyEvent is received)
1221: * and there is no binding for it. Typically this would be some action that inserts text so
1222: * that the keymap doesn't require an action for each possible key.
1223: */
1224: public Action getDefaultAction() {
1225: if (defaultAction != null) {
1226: return defaultAction;
1227: }
1228:
1229: return (parent != null) ? parent.getDefaultAction() : null;
1230: }
1231:
1232: /**
1233: * Set the default action to fire if a key is typed.
1234: */
1235: public void setDefaultAction(Action a) {
1236: defaultAction = a;
1237: }
1238:
1239: public String getName() {
1240: return nm;
1241: }
1242:
1243: public Action getAction(KeyStroke key) {
1244: Action a = bindings.get(key);
1245:
1246: if ((a == null) && (parent != null)) {
1247: a = parent.getAction(key);
1248: }
1249:
1250: return a;
1251: }
1252:
1253: public KeyStroke[] getBoundKeyStrokes() {
1254: KeyStroke[] keys = new KeyStroke[bindings.size()];
1255: int i = 0;
1256:
1257: for (Enumeration<KeyStroke> e = bindings.keys(); e
1258: .hasMoreElements();) {
1259: keys[i++] = e.nextElement();
1260: }
1261:
1262: return keys;
1263: }
1264:
1265: public Action[] getBoundActions() {
1266: Action[] actions = new Action[bindings.size()];
1267: int i = 0;
1268:
1269: for (Enumeration<Action> e = bindings.elements(); e
1270: .hasMoreElements();) {
1271: actions[i++] = e.nextElement();
1272: }
1273:
1274: return actions;
1275: }
1276:
1277: public KeyStroke[] getKeyStrokesForAction(Action a) {
1278: if (a == null) {
1279: return null;
1280: }
1281:
1282: KeyStroke[] retValue = null;
1283:
1284: // Determine local bindings first.
1285: Vector<KeyStroke> keyStrokes = null;
1286:
1287: for (Enumeration<KeyStroke> enum_ = bindings.keys(); enum_
1288: .hasMoreElements();) {
1289: KeyStroke key = enum_.nextElement();
1290:
1291: if (bindings.get(key) == a) {
1292: if (keyStrokes == null) {
1293: keyStrokes = new Vector<KeyStroke>();
1294: }
1295:
1296: keyStrokes.addElement(key);
1297: }
1298: }
1299:
1300: // See if the parent has any.
1301: if (parent != null) {
1302: KeyStroke[] pStrokes = parent.getKeyStrokesForAction(a);
1303:
1304: if (pStrokes != null) {
1305: // Remove any bindings defined in the parent that
1306: // are locally defined.
1307: int rCount = 0;
1308:
1309: for (int counter = pStrokes.length - 1; counter >= 0; counter--) {
1310: if (isLocallyDefined(pStrokes[counter])) {
1311: pStrokes[counter] = null;
1312: rCount++;
1313: }
1314: }
1315:
1316: if ((rCount > 0) && (rCount < pStrokes.length)) {
1317: if (keyStrokes == null) {
1318: keyStrokes = new Vector<KeyStroke>();
1319: }
1320:
1321: for (int counter = pStrokes.length - 1; counter >= 0; counter--) {
1322: if (pStrokes[counter] != null) {
1323: keyStrokes
1324: .addElement(pStrokes[counter]);
1325: }
1326: }
1327: } else if (rCount == 0) {
1328: if (keyStrokes == null) {
1329: retValue = pStrokes;
1330: } else {
1331: retValue = new KeyStroke[keyStrokes.size()
1332: + pStrokes.length];
1333: keyStrokes.copyInto(retValue);
1334: System.arraycopy(pStrokes, 0, retValue,
1335: keyStrokes.size(), pStrokes.length);
1336: keyStrokes = null;
1337: }
1338: }
1339: }
1340: }
1341:
1342: if (keyStrokes != null) {
1343: retValue = new KeyStroke[keyStrokes.size()];
1344: keyStrokes.copyInto(retValue);
1345: }
1346:
1347: return retValue;
1348: }
1349:
1350: public boolean isLocallyDefined(KeyStroke key) {
1351: return bindings.containsKey(key);
1352: }
1353:
1354: public void addActionForKeyStroke(KeyStroke key, Action a) {
1355: bindings.put(key, a);
1356: }
1357:
1358: public void removeKeyStrokeBinding(KeyStroke key) {
1359: bindings.remove(key);
1360: }
1361:
1362: public void removeBindings() {
1363: bindings.clear();
1364: }
1365:
1366: public javax.swing.text.Keymap getResolveParent() {
1367: return parent;
1368: }
1369:
1370: public void setResolveParent(javax.swing.text.Keymap parent) {
1371: this .parent = parent;
1372: }
1373:
1374: /**
1375: * String representation of the keymap... potentially a very long string.
1376: */
1377: @Override
1378: public String toString() {
1379: return super .toString() + "[name=" + nm + ", bindings="
1380: + bindings + "]"; // NOI18N
1381: }
1382: }
1383:
1384: /**
1385: * KeymapWrapper wraps a Keymap inside an InputMap. For KeymapWrapper to be useful it must be
1386: * used with a KeymapActionMap. KeymapWrapper for the most part, is an InputMap with two
1387: * parents. The first parent visited is ALWAYS the Keymap, with the second parent being the
1388: * parent inherited from InputMap. If <code>keymap.getAction</code> returns null, implying the
1389: * Keymap does not have a binding for the KeyStroke, the parent is then visited. If the Keymap
1390: * has a binding, the Action is returned, if not and the KeyStroke represents a KeyTyped event
1391: * and the Keymap has a defaultAction, <code>DefaultActionKey</code> is returned.
1392: * <p>
1393: * KeymapActionMap is then able to transate the object passed in to either message the Keymap,
1394: * or message its default implementation.
1395: */
1396: static class KeymapWrapper extends InputMap {
1397: static final Object DefaultActionKey = new Object();
1398: private Keymap keymap;
1399:
1400: KeymapWrapper(Keymap keymap) {
1401: this .keymap = keymap;
1402: }
1403:
1404: public KeyStroke[] keys() {
1405: KeyStroke[] sKeys = super .keys();
1406: KeyStroke[] keymapKeys = keymap.getBoundKeyStrokes();
1407: int sCount = (sKeys == null) ? 0 : sKeys.length;
1408: int keymapCount = (keymapKeys == null) ? 0
1409: : keymapKeys.length;
1410:
1411: if (sCount == 0) {
1412: return keymapKeys;
1413: }
1414:
1415: if (keymapCount == 0) {
1416: return sKeys;
1417: }
1418:
1419: KeyStroke[] retValue = new KeyStroke[sCount + keymapCount];
1420:
1421: // There may be some duplication here...
1422: System.arraycopy(sKeys, 0, retValue, 0, sCount);
1423: System.arraycopy(keymapKeys, 0, retValue, sCount,
1424: keymapCount);
1425:
1426: return retValue;
1427: }
1428:
1429: public int size() {
1430: // There may be some duplication here...
1431: KeyStroke[] keymapStrokes = keymap.getBoundKeyStrokes();
1432: int keymapCount = (keymapStrokes == null) ? 0
1433: : keymapStrokes.length;
1434:
1435: return super .size() + keymapCount;
1436: }
1437:
1438: public Object get(KeyStroke keyStroke) {
1439: Object retValue = keymap.getAction(keyStroke);
1440:
1441: if (retValue == null) {
1442: retValue = super .get(keyStroke);
1443:
1444: if ((retValue == null)
1445: && (keyStroke.getKeyChar() != KeyEvent.CHAR_UNDEFINED)
1446: && (keymap.getDefaultAction() != null)) {
1447: // Implies this is a KeyTyped event, use the default
1448: // action.
1449: retValue = DefaultActionKey;
1450: }
1451: }
1452:
1453: return retValue;
1454: }
1455: }
1456:
1457: /**
1458: * Wraps a Keymap inside an ActionMap. This is used with a KeymapWrapper. If <code>get</code>
1459: * is passed in <code>KeymapWrapper.DefaultActionKey</code>, the default action is returned,
1460: * otherwise if the key is an Action, it is returned.
1461: */
1462: static class KeymapActionMap extends ActionMap {
1463: private Keymap keymap;
1464:
1465: KeymapActionMap(Keymap keymap) {
1466: this .keymap = keymap;
1467: }
1468:
1469: public Object[] keys() {
1470: Object[] sKeys = super .keys();
1471: Object[] keymapKeys = keymap.getBoundActions();
1472: int sCount = (sKeys == null) ? 0 : sKeys.length;
1473: int keymapCount = (keymapKeys == null) ? 0
1474: : keymapKeys.length;
1475: boolean hasDefault = (keymap.getDefaultAction() != null);
1476:
1477: if (hasDefault) {
1478: keymapCount++;
1479: }
1480:
1481: if (sCount == 0) {
1482: if (hasDefault) {
1483: Object[] retValue = new Object[keymapCount];
1484:
1485: if (keymapCount > 1) {
1486: System.arraycopy(keymapKeys, 0, retValue, 0,
1487: keymapCount - 1);
1488: }
1489:
1490: retValue[keymapCount - 1] = KeymapWrapper.DefaultActionKey;
1491:
1492: return retValue;
1493: }
1494:
1495: return keymapKeys;
1496: }
1497:
1498: if (keymapCount == 0) {
1499: return sKeys;
1500: }
1501:
1502: Object[] retValue = new Object[sCount + keymapCount];
1503:
1504: // There may be some duplication here...
1505: System.arraycopy(sKeys, 0, retValue, 0, sCount);
1506:
1507: if (hasDefault) {
1508: if (keymapCount > 1) {
1509: System.arraycopy(keymapKeys, 0, retValue, sCount,
1510: keymapCount - 1);
1511: }
1512:
1513: retValue[(sCount + keymapCount) - 1] = KeymapWrapper.DefaultActionKey;
1514: } else {
1515: System.arraycopy(keymapKeys, 0, retValue, sCount,
1516: keymapCount);
1517: }
1518:
1519: return retValue;
1520: }
1521:
1522: public int size() {
1523: // There may be some duplication here...
1524: Object[] actions = keymap.getBoundActions();
1525: int keymapCount = (actions == null) ? 0 : actions.length;
1526:
1527: if (keymap.getDefaultAction() != null) {
1528: keymapCount++;
1529: }
1530:
1531: return super .size() + keymapCount;
1532: }
1533:
1534: public Action get(Object key) {
1535: Action retValue = super .get(key);
1536:
1537: if (retValue == null) {
1538: // Try the Keymap.
1539: if (key == KeymapWrapper.DefaultActionKey) {
1540: retValue = keymap.getDefaultAction();
1541: } else if (key instanceof Action) {
1542: // This is a little iffy, technically an Action is
1543: // a valid Key. We're assuming the Action came from
1544: // the InputMap though.
1545: retValue = (Action) key;
1546: }
1547: }
1548:
1549: return retValue;
1550: }
1551: }
1552:
1553: /** #6326882 Minimalistic implementation of <code>AccessibleContext</code>.
1554: * It's needed to find out whether it is sufficient to satisfy A11Y. */
1555: private class AccessibleDesignerPaneBase extends
1556: AccessibleJComponent {
1557: public AccessibleDesignerPaneBase() {
1558: super ();
1559: }
1560: } // End of AccessibleDesignerPaneBase.
1561:
1562: // /** XXX Temporary, until the Document, Range and Position are moved */
1563: // public static DomDocument createDomDocument(WebForm webForm) {
1564: // return new Document(webForm);
1565: // }
1566: //
1567: // /** XXX Temporary, until the Document, Range and Position are moved */
1568: // public static int compareBoundaryPoints(Node endPointA, int offsetA, Node endPointB, int offsetB) {
1569: // return Position.compareBoundaryPoints(endPointA, offsetA, endPointB, offsetB);
1570: // }
1571: //
1572: // public static DomPosition createDomPosition(Node node, int offset, Bias bias) {
1573: // return Position.create(node, offset, bias);
1574: // }
1575: //
1576: // public static DomPosition createDomPosition(Node node, boolean after) {
1577: // return Position.create(node, after);
1578: // }
1579: //
1580: // public static DomPosition first(DomPosition dot, DomPosition mark) {
1581: // return Position.first(dot, mark);
1582: // }
1583: //
1584: // public static DomPosition last(DomPosition dot, DomPosition mark) {
1585: // return Position.last(dot, mark);
1586: // }
1587:
1588: // XXX Moved from DesignerCaret.
1589: public void replaceSelection(String content) {
1590: // WebForm webform = component.getDocument().getWebForm();
1591: WebForm webform = getWebForm();
1592:
1593: // XXX Moving to DefaultKeyTypedAction.
1594: // InlineEditor editor = webform.getManager().getInlineEditor();
1595: // if ((content.equals("\n") || content.equals("\r\n")) // NOI18N
1596: // && (editor != null) && !editor.isMultiLine()) {
1597: // // Commit
1598: // // Should I look to see if the Shift key is pressed, and if so let
1599: // // you insert a newline?
1600: // webform.getManager().finishInlineEditing(false);
1601: // return;
1602: // }
1603:
1604: if (caret == null) {
1605: return;
1606: }
1607: // XXX Moving to DesigneCaret, and designer/jsf/../DomDocumentImpl.
1608: // /*
1609: // if (range.isReadOnlyRegion()) {
1610: // UIManager.getLookAndFeel().provideErrorFeedback(component);
1611: // return;
1612: // }
1613: // */
1614: // if (caret.hasSelection()) {
1615: // caret.removeSelection();
1616: // }
1617: //
1618: //// Position pos = getDot();
1619: // DomPosition pos = caret.getDot();
1620: //
1621: // if (editor == null) {
1622: //// assert (pos == Position.NONE) || !pos.isRendered();
1623: //// if (pos != Position.NONE && MarkupService.isRenderedNode(pos.getNode())) {
1624: // if (pos != DomPosition.NONE && MarkupService.isRenderedNode(pos.getNode())) {
1625: // ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL,
1626: // new IllegalStateException("Node is expected to be not rendered, node=" + pos.getNode())); // NOI18N
1627: // }
1628: // } // else: Stay in the DocumentFragment; don't jump to the source DOM (there is none)
1629: //
1630: //// if (pos == Position.NONE) {
1631: // if (pos == DomPosition.NONE) {
1632: // UIManager.getLookAndFeel().provideErrorFeedback(this);
1633: // return;
1634: // }
1635: //
1636: //// component.getDocument().insertString(this, pos, content);
1637: //// component.getDocument().insertString(pos, content);
1638: // webform.getDomDocument().insertString(pos, content);
1639: boolean beep = !caret.replaceSelection(content);
1640:
1641: if (beep) {
1642: UIManager.getLookAndFeel().provideErrorFeedback(this );
1643: }
1644: }
1645:
1646: // XXX Moved from DesignerCaret.
1647: /** XXX Incorrect impl of cut/copy operation. Revise.
1648: * Return the text in the selection, if any (if not returns null).
1649: * If the cut parameter is true, then the selection is deleted too.
1650: */
1651: public Transferable copySelection(boolean cut) {
1652: if (caret == null) {
1653: return null;
1654: }
1655:
1656: if (caret.hasSelection()) {
1657: // String text = range.getText();
1658: String text = caret.getSelectedText();
1659: // assert text.length() > 0;
1660:
1661: Transferable transferable = new StringSelection(text);
1662:
1663: if (cut) {
1664: caret.removeSelection();
1665: }
1666:
1667: return transferable;
1668: } else {
1669: return new StringSelection(""); // NOI18N
1670: }
1671: }
1672:
1673: public boolean hasCaret() {
1674: return getCaret() != null;
1675: }
1676:
1677: public boolean hasCaretSelection() {
1678: return caret == null ? false : caret.hasSelection();
1679: }
1680:
1681: public DomPosition getFirstPosition() {
1682: return caret == null ? DomPosition.NONE : caret
1683: .getFirstPosition();
1684: }
1685:
1686: public DomPosition getLastPosition() {
1687: return caret == null ? DomPosition.NONE : caret
1688: .getLastPosition();
1689: }
1690:
1691: public boolean isCaretReadOnlyRegion() {
1692: return caret == null ? false : caret.isReadOnlyRegion();
1693: }
1694:
1695: // public boolean isCaretWithinEditableRegion(DomPosition domPosition) {
1696: // return caret == null ? false : caret.isWithinEditableRegion(domPosition);
1697: // }
1698:
1699: public boolean isCaretVisible() {
1700: return caret == null ? false : caret.isVisible();
1701: }
1702:
1703: public DomPosition getCaretDot() {
1704: return caret == null ? DomPosition.NONE : caret.getDot();
1705: }
1706:
1707: public DomPosition getCaretMark() {
1708: return caret == null ? DomPosition.NONE : caret.getMark();
1709: }
1710:
1711: public void createCaret() {
1712: DesignerCaret dc = getUI().createCaret();
1713: setCaret(dc);
1714: }
1715:
1716: public boolean removeNextChar() {
1717: return caret == null ? false : caret.removeNextChar();
1718: }
1719:
1720: public boolean removePreviousChar() {
1721: return caret == null ? false : caret.removePreviousChar();
1722: }
1723:
1724: public DomRange getCaretRange() {
1725: return caret == null ? null : caret.getRange();
1726: }
1727:
1728: public void setCaretDot(DomPosition dot) {
1729: if (caret != null) {
1730: caret.setDot(dot);
1731: }
1732: }
1733:
1734: public void moveCaretDot(DomPosition dot) {
1735: if (caret != null) {
1736: caret.moveDot(dot);
1737: }
1738: }
1739:
1740: public void setCaretVisible(boolean visible) {
1741: if (caret != null) {
1742: caret.setVisible(visible);
1743: }
1744: }
1745:
1746: public void paintCaret(Graphics g) {
1747: if (caret != null) {
1748: caret.paint(g);
1749: }
1750: }
1751:
1752: public void setCaretMagicPosition(Point magicPosition) {
1753: if (caret != null) {
1754: caret.setMagicCaretPosition(magicPosition);
1755: }
1756: }
1757:
1758: public Point getCaretMagicPosition() {
1759: return caret == null ? null : caret.getMagicCaretPosition();
1760: }
1761:
1762: // XXX Strange hack, consequence of messy InteractionManager.
1763: public void mousePressed(MouseEvent evt) {
1764: if (caret != null) {
1765: caret.mousePressed(evt);
1766: }
1767: }
1768:
1769: // XXX Strange hack, consequence of messy InteractionManager.
1770: public void mouseClicked(MouseEvent evt) {
1771: if (caret != null) {
1772: caret.mouseClicked(evt);
1773: }
1774: }
1775:
1776: // XXX Strange hack, consequence of messy InteractionManager.
1777: public void mouseDragged(MouseEvent evt) {
1778: if (caret != null) {
1779: caret.mouseDragged(evt);
1780: }
1781: }
1782:
1783: // XXX Strange hack, consequence of messy InteractionManager.
1784: public void mouseReleased(MouseEvent evt) {
1785: if (caret != null) {
1786: caret.mouseReleased(evt);
1787: }
1788: }
1789:
1790: // XXX ?? Bad architecture.
1791: public void caretDetachDom() {
1792: if (caret != null) {
1793: caret.detachDom();
1794: }
1795: }
1796:
1797: /** XXX Escaping.
1798: * TODO Revise */
1799: public abstract void escape(long when);
1800:
1801: private static void info(String message) {
1802: Logger logger = getLogger();
1803: logger.info(message);
1804: }
1805:
1806: private static Logger getLogger() {
1807: return Logger.getLogger(DesignerPaneBase.class.getName());
1808: }
1809: }
|