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:
0042: package org.netbeans.modules.visualweb.text;
0043:
0044: import java.awt.Graphics;
0045: import java.awt.HeadlessException;
0046: import java.awt.Point;
0047: import java.awt.Rectangle;
0048: import java.awt.Toolkit;
0049: import java.awt.datatransfer.Clipboard;
0050: import java.awt.datatransfer.ClipboardOwner;
0051: import java.awt.datatransfer.StringSelection;
0052: import java.awt.datatransfer.Transferable;
0053: import java.awt.event.ActionEvent;
0054: import java.awt.event.ActionListener;
0055: import java.awt.event.FocusEvent;
0056: import java.awt.event.FocusListener;
0057: import java.awt.event.MouseEvent;
0058: import java.awt.event.MouseListener;
0059: import java.awt.event.MouseMotionListener;
0060: import javax.swing.Action;
0061: import javax.swing.ActionMap;
0062: import javax.swing.SwingUtilities;
0063: import javax.swing.Timer;
0064: import javax.swing.TransferHandler;
0065: import javax.swing.UIManager;
0066:
0067: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomDocument;
0068: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomDocumentEvent;
0069: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomDocumentListener;
0070: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomPosition;
0071: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomRange;
0072: import org.netbeans.modules.visualweb.api.designer.markup.MarkupService;
0073: import org.netbeans.modules.visualweb.css2.ModelViewMapper;
0074: import org.netbeans.modules.visualweb.designer.WebForm;
0075: import org.netbeans.modules.visualweb.text.actions.SelectLineAction;
0076: import org.netbeans.modules.visualweb.text.actions.SelectWordAction;
0077:
0078: import org.openide.ErrorManager;
0079:
0080: import org.openide.util.WeakListeners;
0081: import org.w3c.dom.Node;
0082:
0083: /**
0084: * Caret to use in flow mode editing in the designer.
0085: * Derived from Swing's DesignerCaret code but changed integer dot/mark
0086: * based code to use DOM Range objects, changed document update semantics,
0087: * and mouse operations since we can't blindly listen for mouse clicks
0088: * we have to take directions from the SelectionManager.
0089: *
0090: * @todo Stop using Position objects in calls to modelToView etc;
0091: * instead pass node and offset.
0092: *
0093: * @author Timothy Prinzing
0094: * @author Tor Norbye
0095: */
0096: /*public*/class DesignerCaret extends Rectangle implements
0097: FocusListener, MouseListener, MouseMotionListener {
0098: private static transient Action selectWord = null;
0099: private static transient Action selectLine = null;
0100: private transient boolean installed;
0101:
0102: // ---- member variables ------------------------------------------
0103: // package-private to avoid inner classes private member
0104: // access bug
0105: DesignerPaneBase component;
0106:
0107: /**
0108: * flag to indicate if async updates should move the caret.
0109: */
0110: boolean async;
0111: boolean visible;
0112: boolean flashOn;
0113:
0114: //Position dot = Position.NONE;
0115: //Position mark = Position.NONE;
0116: // Range range = null;
0117: DomRange range = null;
0118:
0119: Object selectionTag;
0120: boolean selectionVisible;
0121: Timer flasher;
0122: Point magicCaretPosition;
0123: private final Handler handler = new Handler();
0124:
0125: /**
0126: * This is used to indicate if the caret currently owns the selection. This is always false if
0127: * the system does not support the system clipboard.
0128: */
0129: private boolean ownsSelection;
0130:
0131: /**
0132: * If this is true, the location of the dot is updated regardless of the current location. This
0133: * is set in the DocumentListener such that even if the model location of dot hasn't changed
0134: * (perhaps do to a forward delete) the visual location is updated.
0135: */
0136: private boolean forceCaretPositionChange;
0137:
0138: /**
0139: * Whether or not mouseReleased should adjust the caret and focus. This flag is set by
0140: * mousePressed if it wanted to adjust the caret and focus but couldn't because of a possible
0141: * DnD operation.
0142: */
0143: private transient boolean shouldHandleRelease;
0144:
0145: /**
0146: * Constructs a default caret.
0147: */
0148: public DesignerCaret() {
0149: async = false;
0150: }
0151:
0152: public DomRange getRange() {
0153: return range;
0154: }
0155:
0156: /**
0157: * Get the flag that determines whether or not asynchronous updates will move the caret.
0158: * Normally the caret is moved by events from the event thread such as mouse or keyboard events.
0159: * Changes from another thread might be used to load a file, or show changes from another user.
0160: * This flag determines whether those changes will move the caret.
0161: */
0162: boolean getAsynchronousMovement() {
0163: return async;
0164: }
0165:
0166: /**
0167: * Set the flag that determines whether or not asynchronous updates will move the caret.
0168: * Normally the caret is moved by events from the event thread such as mouse or keyboard events.
0169: * Changes from another thread might be used to load a file, or show changes from another user.
0170: * This flag determines whether those changes will move the caret.
0171: *
0172: * @param m
0173: * move the caret on asynchronous updates if true.
0174: */
0175: void setAsynchronousMovement(boolean m) {
0176: async = m;
0177: }
0178:
0179: /**
0180: * Gets the text editor component that this caret is is bound to.
0181: *
0182: * @return the component
0183: */
0184: protected final DesignerPaneBase getComponent() {
0185: return component;
0186: }
0187:
0188: /**
0189: * Cause the caret to be painted. The repaint area is the bounding box of the caret (i.e. the
0190: * caret rectangle or <em>this</em>).
0191: * <p>
0192: * This method is thread safe, although most Swing methods are not. Please see <A
0193: * HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html"> Threads and Swing </A>
0194: * for more information.
0195: */
0196: protected final synchronized void repaint() {
0197: if (component != null) {
0198: component.repaint(x, y, width, height);
0199: }
0200: }
0201:
0202: /**
0203: * Cause the selection region to be painted.
0204: */
0205: protected final synchronized void repaintSelection() {
0206: if (component != null) {
0207: component.repaint();
0208: }
0209: }
0210:
0211: /**
0212: * Damages the area surrounding the caret to cause it to be repainted in a new location. If
0213: * paint() is reimplemented, this method should also be reimplemented. This method should update
0214: * the caret bounds (x, y, width, and height).
0215: *
0216: * @param r
0217: * the current location of the caret
0218: * @see #paint
0219: */
0220: protected synchronized void damage(Rectangle r) {
0221: if (r != null) {
0222: x = r.x - 4;
0223: y = r.y;
0224: width = 10;
0225: height = r.height;
0226: repaint();
0227: }
0228: }
0229:
0230: /**
0231: * Scrolls the associated view (if necessary) to make the caret visible. Since how this should
0232: * be done is somewhat of a policy, this method can be reimplemented to change the behavior. By
0233: * default the scrollRectToVisible method is called on the associated component.
0234: *
0235: * @param nloc
0236: * the new position to scroll to
0237: */
0238: protected void adjustVisibility(Rectangle nloc) {
0239: if (component == null) {
0240: return;
0241: }
0242:
0243: if (SwingUtilities.isEventDispatchThread()) {
0244: component.scrollRectToVisible(nloc);
0245: } else {
0246: SwingUtilities.invokeLater(new SafeScroller(nloc));
0247: }
0248: }
0249:
0250: /**
0251: * Tries to set the position of the caret from the coordinates of a mouse event, using
0252: * viewToModel().
0253: *
0254: * @param e
0255: * the mouse event
0256: */
0257: private/*protected*/void positionCaret(MouseEvent e) { // XXX When is this used?
0258:
0259: Point pt = new Point(e.getX(), e.getY());
0260: // Position pos = component.getUI().viewToModel(component, pt);
0261: // WebForm webform = component.getDocument().getWebForm();
0262: WebForm webform = component.getWebForm();
0263:
0264: // Position pos = webform.viewToModel(pt);
0265: DomPosition pos = webform.viewToModel(pt);
0266:
0267: if ((webform.getManager().getInlineEditor() == null)
0268: || !webform.getManager().getInlineEditor()
0269: .isDocumentEditor()) {
0270: // pos = DesignerUtils.checkPosition(pos, true, /*webform*/webform.getManager().getInlineEditor());
0271: // pos = ModelViewMapper.findValidPosition(pos, true, /*webform*/webform.getManager().getInlineEditor());
0272: pos = ModelViewMapper.findValidPosition(webform, pos, true, /*webform*/
0273: webform.getManager().getInlineEditor());
0274: }
0275:
0276: // if (pos != Position.NONE) {
0277: if (pos != DomPosition.NONE) {
0278: setDot(pos);
0279: }
0280: }
0281:
0282: /**
0283: * Tries to move the position of the caret from the coordinates of a mouse event, using
0284: * viewToModel(). This will cause a selection if the dot and mark are different.
0285: *
0286: * @param e
0287: * the mouse event
0288: */
0289: private/*protected*/void moveCaret(MouseEvent e) {
0290: Point pt = new Point(e.getX(), e.getY());
0291: // Position pos = component.getUI().viewToModel(component, pt);
0292: // WebForm webform = component.getDocument().getWebForm();
0293: WebForm webform = component.getWebForm();
0294:
0295: // Position pos = webform.viewToModel(pt);
0296: DomPosition pos = webform.viewToModel(pt);
0297:
0298: if ((webform.getManager().getInlineEditor() == null)
0299: || !webform.getManager().getInlineEditor()
0300: .isDocumentEditor()) {
0301: // pos = DesignerUtils.checkPosition(pos, true, /*webform*/webform.getManager().getInlineEditor());
0302: // pos = ModelViewMapper.findValidPosition(pos, true, /*webform*/webform.getManager().getInlineEditor());
0303: pos = ModelViewMapper.findValidPosition(webform, pos, true, /*webform*/
0304: webform.getManager().getInlineEditor());
0305: }
0306:
0307: // if (pos != Position.NONE) {
0308: if (pos != DomPosition.NONE) {
0309: moveDot(pos);
0310: }
0311: }
0312:
0313: // --- FocusListener methods --------------------------
0314:
0315: /**
0316: * Called when the component containing the caret gains focus. This is implemented to set the
0317: * caret to visible if the component is editable.
0318: *
0319: * @param e
0320: * the focus event
0321: * @see FocusListener#focusGained
0322: */
0323: public void focusGained(FocusEvent e) {
0324: if (component.isEnabled()) {
0325: setVisible(true);
0326: setSelectionVisible(true);
0327: }
0328: }
0329:
0330: /**
0331: * Called when the component containing the caret loses focus. This is implemented to set the
0332: * caret to visibility to false.
0333: *
0334: * @param e
0335: * the focus event
0336: * @see FocusListener#focusLost
0337: */
0338: public void focusLost(FocusEvent e) {
0339: setVisible(false);
0340: setSelectionVisible(ownsSelection || e.isTemporary());
0341: }
0342:
0343: // --- MouseListener methods -----------------------------------
0344:
0345: /**
0346: * Called when the mouse is clicked. If the click was generated from button1, a double click
0347: * selects a word, and a triple click the current line.
0348: *
0349: * @param e
0350: * the mouse event
0351: * @see MouseListener#mouseClicked
0352: */
0353: public void mouseClicked(MouseEvent e) {
0354: if (!e.isConsumed()) {
0355: int nclicks = e.getClickCount();
0356:
0357: if (SwingUtilities.isLeftMouseButton(e)) {
0358: // mouse 1 behavior
0359: if (e.getClickCount() == 2) {
0360: Action a = null;
0361: ActionMap map = getComponent().getActionMap();
0362:
0363: if (map != null) {
0364: a = map.get(DesignerPaneBase.selectWordAction);
0365: }
0366:
0367: if (a == null) {
0368: if (selectWord == null) {
0369: selectWord = new SelectWordAction();
0370: }
0371:
0372: a = selectWord;
0373: }
0374:
0375: a.actionPerformed(new ActionEvent(getComponent(),
0376: ActionEvent.ACTION_PERFORMED, null, e
0377: .getWhen(), e.getModifiers()));
0378: } else if (e.getClickCount() == 3) {
0379: Action a = null;
0380: ActionMap map = getComponent().getActionMap();
0381:
0382: if (map != null) {
0383: a = map.get(DesignerPaneBase.selectLineAction);
0384: }
0385:
0386: if (a == null) {
0387: if (selectLine == null) {
0388: selectLine = new SelectLineAction();
0389: }
0390:
0391: a = selectLine;
0392: }
0393:
0394: a.actionPerformed(new ActionEvent(getComponent(),
0395: ActionEvent.ACTION_PERFORMED, null, e
0396: .getWhen(), e.getModifiers()));
0397: }
0398: } else if (SwingUtilities.isMiddleMouseButton(e)) {
0399: // mouse 2 behavior
0400: if (nclicks == 1) {
0401: // paste system selection, if it exists
0402: DesignerPaneBase c = (DesignerPaneBase) e
0403: .getSource();
0404:
0405: if (c != null) {
0406: try {
0407: Toolkit tk = c.getToolkit();
0408: Clipboard buffer = tk.getSystemSelection();
0409:
0410: if (buffer != null) {
0411: // platform supports system selections, update it.
0412: adjustCaret(e);
0413:
0414: TransferHandler th = c
0415: .getTransferHandler();
0416:
0417: if (th != null) {
0418: Transferable trans = buffer
0419: .getContents(null);
0420:
0421: if (trans != null) {
0422: th.importData(c, trans);
0423: }
0424: }
0425:
0426: adjustFocus(true);
0427: }
0428: } catch (HeadlessException he) {
0429: ErrorManager.getDefault().notify(
0430: ErrorManager.INFORMATIONAL, he);
0431:
0432: // do nothing... there is no system clipboard
0433: }
0434: }
0435: }
0436: }
0437: }
0438: }
0439:
0440: /**
0441: * If button 1 is pressed, this is implemented to request focus on the associated text
0442: * component, and to set the caret position. If the shift key is held down, the caret will be
0443: * moved, potentially resulting in a selection, otherwise the caret position will be set to the
0444: * new location. If the component is not enabled, there will be no request for focus.
0445: *
0446: * @param e
0447: * the mouse event
0448: * @see MouseListener#mousePressed
0449: */
0450: public void mousePressed(MouseEvent e) {
0451: if (SwingUtilities.isLeftMouseButton(e)) {
0452: if (e.isConsumed()) {
0453: shouldHandleRelease = true;
0454: } else {
0455: shouldHandleRelease = false;
0456: adjustCaretAndFocus(e);
0457: }
0458: }
0459: }
0460:
0461: private void adjustCaretAndFocus(MouseEvent e) {
0462: adjustCaret(e);
0463: adjustFocus(false);
0464: }
0465:
0466: /**
0467: * Adjusts the caret location based on the MouseEvent.
0468: */
0469: private void adjustCaret(MouseEvent e) {
0470: // if (((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0) && (getDot() != Position.NONE)) {
0471: if (((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0)
0472: && (getDot() != DomPosition.NONE)) {
0473: moveCaret(e);
0474: } else {
0475: positionCaret(e);
0476: }
0477: }
0478:
0479: /**
0480: * Adjusts the focus, if necessary.
0481: *
0482: * @param inWindow
0483: * if true indicates requestFocusInWindow should be used
0484: */
0485: private void adjustFocus(boolean inWindow) {
0486: if ((component != null) && component.isEnabled()
0487: && component.isRequestFocusEnabled()) {
0488: if (inWindow) {
0489: component.requestFocusInWindow();
0490: } else {
0491: component.requestFocus();
0492: }
0493: }
0494: }
0495:
0496: /**
0497: * Called when the mouse is released.
0498: *
0499: * @param e
0500: * the mouse event
0501: * @see MouseListener#mouseReleased
0502: */
0503: public void mouseReleased(MouseEvent e) {
0504: if (shouldHandleRelease && SwingUtilities.isLeftMouseButton(e)) {
0505: adjustCaretAndFocus(e);
0506: }
0507: }
0508:
0509: /**
0510: * Called when the mouse enters a region.
0511: *
0512: * @param e
0513: * the mouse event
0514: * @see MouseListener#mouseEntered
0515: */
0516: public void mouseEntered(MouseEvent e) {
0517: }
0518:
0519: /**
0520: * Called when the mouse exits a region.
0521: *
0522: * @param e
0523: * the mouse event
0524: * @see MouseListener#mouseExited
0525: */
0526: public void mouseExited(MouseEvent e) {
0527: }
0528:
0529: // --- MouseMotionListener methods -------------------------
0530:
0531: /**
0532: * Moves the caret position according to the mouse pointer's current location. This effectively
0533: * extends the selection. By default, this is only done for mouse button 1.
0534: *
0535: * @param e
0536: * the mouse event
0537: * @see MouseMotionListener#mouseDragged
0538: */
0539: public void mouseDragged(MouseEvent e) {
0540: if ((!e.isConsumed()) && SwingUtilities.isLeftMouseButton(e)) {
0541: moveCaret(e);
0542: }
0543: }
0544:
0545: /**
0546: * Called when the mouse is moved.
0547: *
0548: * @param e
0549: * the mouse event
0550: * @see MouseMotionListener#mouseMoved
0551: */
0552: public void mouseMoved(MouseEvent e) {
0553: }
0554:
0555: // ---- Caret methods ---------------------------------
0556:
0557: /**
0558: * Renders the caret as a vertical line. If this is reimplemented the damage method should also
0559: * be reimplemented as it assumes the shape of the caret is a vertical line. Sets the caret
0560: * color to the value returned by getCaretColor().
0561: * <p>
0562: *
0563: * @param g
0564: * the graphics context
0565: * @see #damage
0566: */
0567: public void paint(Graphics g) {
0568: if (isVisible() && flashOn) {
0569: DesignerPaneBaseUI mapper = component.getUI();
0570:
0571: if (range == null) {
0572: return;
0573: }
0574:
0575: // Position dot = range.getDot();
0576: DomPosition dot = range.getDot();
0577: // Rectangle r = mapper.modelToView(/*component,*/ dot);
0578: // WebForm webForm = component.getDocument().getWebForm();
0579: WebForm webForm = component.getWebForm();
0580:
0581: Rectangle r = webForm.modelToView(dot);
0582:
0583: if ((r == null) || ((r.width == 0) && (r.height == 0))) {
0584: return;
0585: }
0586:
0587: if ((width > 0) && (height > 0)
0588: && !this ._contains(r.x, r.y, r.width, r.height)) {
0589: // We seem to have gotten out of sync and no longer
0590: // contain the right location, adjust accordingly.
0591: Rectangle clip = g.getClipBounds();
0592:
0593: if ((clip != null) && !clip.contains(this )) {
0594: // Clip doesn't contain the old location, force it
0595: // to be repainted lest we leave a caret around.
0596: repaint();
0597: }
0598:
0599: // This will potentially cause a repaint of something
0600: // we're already repainting, but without changing the
0601: // semantics of damage we can't really get around this.
0602: damage(r);
0603: }
0604:
0605: g.setColor(component.getCaretColor());
0606:
0607: //g.setColor(java.awt.Color.RED);
0608: g.drawLine(r.x, r.y, r.x, (r.y + r.height) - 1);
0609: g.drawLine(r.x + 1, r.y, r.x + 1, (r.y + r.height) - 1);
0610:
0611: // see if we should paint a flag to indicate the bias
0612: // of the caret.
0613: // PENDING(prinz) this should be done through
0614: // protected methods so that alternative LAF
0615: // will show bidi information.
0616: }
0617: }
0618:
0619: /**
0620: * Called when the UI is being installed into the interface of a JTextComponent. This can be
0621: * used to gain access to the model that is being navigated by the implementation of this
0622: * interface. Sets the dot and mark to 0, and establishes document, property change, focus,
0623: * mouse, and mouse motion listeners.
0624: *
0625: * @param c
0626: * the component
0627: */
0628: public void install(DesignerPaneBase c) {
0629: // assert !installed;
0630: if (installed) {
0631: ErrorManager
0632: .getDefault()
0633: .notify(
0634: ErrorManager.INFORMATIONAL,
0635: new IllegalStateException(
0636: "The designer caret is installed already!")); // NOI18N
0637: return;
0638: }
0639:
0640: installed = true;
0641: component = c;
0642:
0643: // NO caret until you click to set one (switching to flow mode
0644: // should also do that
0645: //dot = mark = Position.NONE;
0646: c.addFocusListener(this );
0647:
0648: // c.getDocument().addDomDocumentListener(handler);
0649: // c.getWebForm().getDocument().addDomDocumentListener(handler);
0650: // XXX #123003 Another memory leak via the listener (missing call to deinstall), using weak listener instead.
0651: // c.getWebForm().getDomDocument().addDomDocumentListener(handler);
0652: DomDocument domDocument = c.getWebForm().getDomDocument();
0653: DomDocumentListener weakListener = WeakListeners.create(
0654: DomDocumentListener.class, handler, domDocument);
0655: domDocument.addDomDocumentListener(weakListener);
0656:
0657: // if the component already has focus, it won't
0658: // be notified.
0659: if (component.hasFocus()) {
0660: focusGained(null);
0661: }
0662: }
0663:
0664: /**
0665: * Called when the UI is being removed from the interface of a JTextComponent. This is used to
0666: * unregister any listeners that were attached.
0667: *
0668: * @param c
0669: * the component
0670: */
0671: public void deinstall(DesignerPaneBase c) {
0672: // assert installed;
0673: if (!installed) {
0674: ErrorManager
0675: .getDefault()
0676: .notify(
0677: ErrorManager.INFORMATIONAL,
0678: new IllegalStateException(
0679: "The designer caret was not installed before!")); // NOI18N
0680: return;
0681: }
0682:
0683: installed = false;
0684: c.removeFocusListener(this );
0685:
0686: // c.getDocument().removeDomDocumentListener(handler);
0687: // c.getWebForm().getDocument().removeDomDocumentListener(handler);
0688: // c.getWebForm().getDomDocument().removeDomDocumentListener(handler);
0689:
0690: synchronized (this ) {
0691: component = null;
0692: }
0693:
0694: if (flasher != null) {
0695: flasher.stop();
0696: flasher = null;
0697: }
0698:
0699: if (range != null) {
0700: range.detach();
0701: range = null;
0702: }
0703: }
0704:
0705: /**
0706: * Changes the selection visibility.
0707: *
0708: * @param vis
0709: * the new visibility
0710: */
0711: public void setSelectionVisible(boolean vis) {
0712: if (vis != selectionVisible) {
0713: selectionVisible = vis;
0714:
0715: if (!selectionVisible || hasSelection()) {
0716: // XXX this causes a global repaint... notagood!
0717: repaintSelection();
0718: }
0719: }
0720: }
0721:
0722: /**
0723: * Checks whether the current selection is visible.
0724: *
0725: * @return true if the selection is visible
0726: */
0727: public boolean isSelectionVisible() {
0728: return selectionVisible;
0729: }
0730:
0731: /**
0732: * Determines if the caret is currently visible.
0733: *
0734: * @return true if visible else false
0735: */
0736: public boolean isVisible() {
0737: return visible;
0738: }
0739:
0740: /**
0741: * Sets the caret visibility, and repaints the caret.
0742: *
0743: * @param e
0744: * the visibility specifier
0745: */
0746: public void setVisible(boolean e) {
0747: // focus lost notification can come in later after the
0748: // caret has been deinstalled, in which case the component
0749: // will be null.
0750: if (component != null) {
0751: DesignerPaneBaseUI mapper = component.getUI();
0752:
0753: if (visible != e) {
0754: visible = e;
0755:
0756: // repaint the caret
0757: if (range != null) {
0758: // Position dot = range.getDot();
0759: DomPosition dot = range.getDot();
0760: // Rectangle loc = mapper.modelToView(/*component,*/ dot);
0761: // WebForm webForm = component.getDocument().getWebForm();
0762: WebForm webForm = component.getWebForm();
0763:
0764: Rectangle loc = webForm.modelToView(dot);
0765: damage(loc);
0766: }
0767: }
0768: }
0769:
0770: if (flasher != null) {
0771: if (visible) {
0772: flashOn = true;
0773: flasher.start();
0774: } else {
0775: flasher.stop();
0776: }
0777: }
0778: }
0779:
0780: /**
0781: * Sets the caret blink rate.
0782: *
0783: * @param rate
0784: * the rate in milliseconds, 0 to stop blinking
0785: */
0786: public void setBlinkRate(int rate) {
0787: if (rate != 0) {
0788: if (flasher == null) {
0789: flasher = new Timer(rate, handler);
0790: }
0791:
0792: flasher.setDelay(rate);
0793: } else {
0794: if (flasher != null) {
0795: flasher.stop();
0796: flasher.removeActionListener(handler);
0797: flasher = null;
0798: flashOn = true;
0799: }
0800: }
0801: }
0802:
0803: /**
0804: * Gets the caret blink rate.
0805: *
0806: * @return the delay in milliseconds. If this is zero the caret will not blink.
0807: */
0808: public int getBlinkRate() {
0809: return (flasher == null) ? 0 : flasher.getDelay();
0810: }
0811:
0812: /**
0813: * Fetches the current position of the caret.
0814: * The caret dot position is always a source position.
0815: *
0816: * @return the position >= 0
0817: */
0818: // public Position getDot() {
0819: public DomPosition getDot() {
0820: if (range == null) {
0821: // return Position.NONE;
0822: return DomPosition.NONE;
0823: }
0824:
0825: return range.getDot();
0826: }
0827:
0828: /**
0829: * Fetches the current position of the mark. If there is a selection, the dot and mark will not
0830: * be the same.
0831: * The caret mark position is always a source position.
0832: *
0833: * @return the position >= 0
0834: */
0835: // public Position getMark() {
0836: public DomPosition getMark() {
0837: if (range == null) {
0838: // return Position.NONE;
0839: return DomPosition.NONE;
0840: }
0841:
0842: return range.getMark();
0843: }
0844:
0845: /**
0846: * Return the first endpoint of the range in the document.
0847: * This is always a source position.
0848: */
0849: // public Position getFirstPosition() {
0850: public DomPosition getFirstPosition() {
0851: if (range == null) {
0852: // return Position.NONE;
0853: return DomPosition.NONE;
0854: }
0855:
0856: return range.getFirstPosition();
0857: }
0858:
0859: /**
0860: * Return the second/last endpoint of the range in the document.
0861: * This is always a source position.
0862: */
0863: // public Position getLastPosition() {
0864: public DomPosition getLastPosition() {
0865: if (range == null) {
0866: // return Position.NONE;
0867: return DomPosition.NONE;
0868: }
0869:
0870: return range.getLastPosition();
0871: }
0872:
0873: /**
0874: * Sets the caret position and mark to some position. This implicitly sets the selection range
0875: * to zero.
0876: *
0877: * @param dot
0878: * the position >= 0
0879: */
0880: // public void setDot(Position dot) {
0881: public void setDot(DomPosition dot) {
0882: handleSetDot(dot);
0883: }
0884:
0885: /**
0886: * Moves the caret position to some other position.
0887: *
0888: * @param dot
0889: * the position >= 0
0890: */
0891: // public void moveDot(Position dot) {
0892: public void moveDot(DomPosition dot) {
0893: /*
0894: * We don't have disabled designer panes... if (! component.isEnabled()) { // don't allow
0895: * selection on disabled components. setDot(dot, dotBias); return; }
0896: */
0897: // if ((dot == Position.NONE) && (range == null)) {
0898: if ((dot == DomPosition.NONE) && (range == null)) {
0899: return;
0900: } else if ((range != null) && range.isDot(dot)) {
0901: return;
0902: }
0903:
0904: handleMoveDot(dot);
0905: }
0906:
0907: // void handleMoveDot(Position dot) {
0908: void handleMoveDot(DomPosition dot) {
0909: // assert !dot.isRendered();
0910: // if (MarkupService.isRenderedNode(dot.getNode())) {
0911: if (component.getWebForm().isRenderedNode(dot.getNode())) {
0912: ErrorManager.getDefault().notify(
0913: ErrorManager.INFORMATIONAL,
0914: new IllegalArgumentException(
0915: "The node is expected not rendered"
0916: + dot.getNode())); // NOI18N
0917: }
0918:
0919: // XXX Very suspicious assertion.
0920: // assert component.getDocument().getWebForm().getPane().getPageBox().getElement().getOwnerDocument() == component.getDocument().getWebForm().getJspDom();
0921: // if (component.getDocument().getWebForm().getPane().getPageBox().getElement().getOwnerDocument() != component.getDocument().getWebForm().getHtmlDom()) {
0922: // ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL,
0923: // new IllegalStateException("Owner document is expected to be html dom=" + component.getDocument().getWebForm().getHtmlDom() // NOI18N
0924: // + ", but it is dom=" + component.getDocument().getWebForm().getPane().getPageBox().getElement().getOwnerDocument())); // NOI18N
0925: // }
0926: if (component.getWebForm().getPane().getPageBox().getElement()
0927: .getOwnerDocument() != component.getWebForm()
0928: .getHtmlDom()) {
0929: ErrorManager.getDefault().notify(
0930: ErrorManager.INFORMATIONAL,
0931: new IllegalStateException(
0932: "Owner document is expected to be html dom="
0933: + component.getWebForm()
0934: .getHtmlDom() // NOI18N
0935: + ", but it is dom="
0936: + component.getWebForm().getPane()
0937: .getPageBox().getElement()
0938: .getOwnerDocument())); // NOI18N
0939: }
0940:
0941: changeCaretPosition(dot);
0942:
0943: setSelectionVisible(hasSelection());
0944:
0945: if (hasSelection()) {
0946: repaintSelection();
0947: }
0948: }
0949:
0950: // void handleSetDot(Position dot) {
0951: void handleSetDot(DomPosition dot) {
0952: // move dot, if it changed
0953:
0954: /*
0955: * Document doc = component.getDocument(); if (doc != null) { dot = Position.first(dot,
0956: * doc.getEndPosition()); } dot = Position.last(dot, doc.getStartPosition());
0957: */
0958: // XXX #124732 Possible NPE.
0959: // if (dot == Position.NONE) {
0960: if (component == null || dot == null || dot == DomPosition.NONE) {
0961: if (range != null) {
0962: range.detach();
0963: range = null;
0964: }
0965:
0966: // TODO: Gotta clear selection highlights here!
0967: return;
0968: }
0969:
0970: // assert !dot.isRendered() ||
0971: // component.getDocument().getWebForm().getManager().isInlineEditing() : dot;
0972: // if (MarkupService.isRenderedNode(dot.getNode())
0973: if (component.getWebForm().isRenderedNode(dot.getNode())
0974: // && !component.getDocument().getWebForm().getManager().isInlineEditing()) {
0975: && !component.getWebForm().getManager()
0976: .isInlineEditing()) {
0977: ErrorManager.getDefault().notify(
0978: ErrorManager.INFORMATIONAL,
0979: new IllegalStateException(
0980: "It is not in inline editing, and node is rendered node="
0981: + dot.getNode())); // NOI18N
0982: }
0983:
0984: // XXX Very suspicious assertion.
0985: // assert component.getDocument().getWebForm().getPane().getPageBox().getElement().getOwnerDocument() == component.getDocument().getWebForm().getJspDom();
0986: // if (component.getDocument().getWebForm().getPane().getPageBox().getElement().getOwnerDocument() != component.getDocument().getWebForm().getHtmlDom()) {
0987: // ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL,
0988: // new IllegalStateException("Owner document is expected to be html dom=" + component.getDocument().getWebForm().getHtmlDom() // NOI18N
0989: // + ", but it is dom=" + component.getDocument().getWebForm().getPane().getPageBox().getElement().getOwnerDocument())); // NOI18N
0990: // }
0991: if (component.getWebForm().getPane().getPageBox().getElement()
0992: .getOwnerDocument() != component.getWebForm()
0993: .getHtmlDom()) {
0994: ErrorManager.getDefault().notify(
0995: ErrorManager.INFORMATIONAL,
0996: new IllegalStateException(
0997: "Owner document is expected to be html dom="
0998: + component.getWebForm()
0999: .getHtmlDom() // NOI18N
1000: + ", but it is dom="
1001: + component.getWebForm().getPane()
1002: .getPageBox().getElement()
1003: .getOwnerDocument())); // NOI18N
1004: }
1005:
1006: Node node = dot.getNode();
1007: int offset = dot.getOffset();
1008:
1009: // Set the mark to the new dot position
1010: boolean dotChanged;
1011:
1012: if (range == null) {
1013: // range = new Range(component.getDocument().getWebForm(), node, offset, node, offset);
1014: // range = Range.create(component.getDocument().getWebForm(), node, offset, node, offset);
1015: // range = Range.create(component.getWebForm(), node, offset, node, offset);
1016: range = component.getWebForm().createDomRange(node, offset,
1017: node, offset);
1018:
1019: dotChanged = true;
1020: } else {
1021: range.setMark(node, offset, dot.getBias());
1022:
1023: // dotChanged = range.isDot(dot);
1024: // When we're using dom ranges they offset will always
1025: // have been preadjusted so we don't
1026: dotChanged = true;
1027: }
1028:
1029: if (dotChanged || (selectionTag != null)
1030: || forceCaretPositionChange) {
1031: changeCaretPosition(dot);
1032: }
1033:
1034: setSelectionVisible(hasSelection());
1035:
1036: // if (System.getProperty("rave.debugLayout") != null) {
1037: // org.openide.awt.StatusDisplayer.getDefault().setStatusText("Caret=" + getDot());
1038: // }
1039: }
1040:
1041: // ---- local methods --------------------------------------------
1042:
1043: /**
1044: * Sets the caret position (dot) to a new location. This causes the old and new location to be
1045: * repainted. It also makes sure that the caret is within the visible region of the view, if the
1046: * view is scrollable.
1047: */
1048: // void changeCaretPosition(Position dot) {
1049: void changeCaretPosition(DomPosition dot) {
1050: // repaint the old position and set the new value of
1051: // the dot.
1052: repaint();
1053:
1054: // Make sure the caret is visible if this window has the focus.
1055: if ((flasher != null) && flasher.isRunning()) {
1056: flashOn = true;
1057: flasher.restart();
1058: }
1059:
1060: // notify listeners at the caret moved
1061: //this.dot = dot;
1062: //NPE: Why can range be null sometimes
1063: // XXX #94270 I would like that know too. For now just hacking the issue.
1064: // range.setDot(dot.getNode(), dot.getOffset(), dot.getBias());
1065: if (range == null) {
1066: // XXX Log the problem?
1067: // range = new Range(component.getDocument().getWebForm(), dot.getNode(), dot.getOffset(), dot.getNode(), dot.getOffset());
1068: // range = Range.create(component.getDocument().getWebForm(), dot.getNode(), dot.getOffset(), dot.getNode(), dot.getOffset());
1069: // range = Range.create(component.getWebForm(), dot.getNode(), dot.getOffset(), dot.getNode(), dot.getOffset());
1070: range = component.getWebForm().createDomRange(
1071: dot.getNode(), dot.getOffset(), dot.getNode(),
1072: dot.getOffset());
1073: } else {
1074: range.setDot(dot.getNode(), dot.getOffset(), dot.getBias());
1075: }
1076:
1077: updateSystemSelection();
1078:
1079: setMagicCaretPosition(null);
1080:
1081: // We try to repaint the caret later, since things
1082: // may be unstable at the time this is called
1083: // (i.e. we don't want to depend upon notification
1084: // order or the fact that this might happen on
1085: // an unsafe thread).
1086: Runnable callRepaintNewCaret = new Runnable() {
1087: public void run() {
1088: repaintNewCaret();
1089: }
1090: };
1091:
1092: SwingUtilities.invokeLater(callRepaintNewCaret);
1093: }
1094:
1095: /**
1096: * Repaints the new caret position, with the assumption that this is happening on the event
1097: * thread so that calling <code>modelToView</code> is safe.
1098: */
1099: void repaintNewCaret() {
1100: if (component != null) {
1101: DesignerPaneBaseUI mapper = component.getUI();
1102: // Document doc = component.getDocument();
1103:
1104: if ((mapper != null) /*&& (doc != null)*/
1105: && (range != null)) {
1106: // determine the new location and scroll if
1107: // not visible.
1108: // Position dot = range.getDot();
1109: DomPosition dot = range.getDot();
1110: // Rectangle newLoc = mapper.modelToView(/*component,*/ dot);
1111: // WebForm webForm = component.getDocument().getWebForm();
1112: WebForm webForm = component.getWebForm();
1113:
1114: Rectangle newLoc = webForm.modelToView(dot);
1115:
1116: if (newLoc != null) {
1117: adjustVisibility(newLoc);
1118:
1119: // If there is no magic caret position, make one
1120: if (getMagicCaretPosition() == null) {
1121: setMagicCaretPosition(new Point(newLoc.x,
1122: newLoc.y));
1123: }
1124: }
1125:
1126: // repaint the new position
1127: damage(newLoc);
1128: }
1129: }
1130: }
1131:
1132: private void updateSystemSelection() {
1133: if (!range.isEmpty() && (component != null)) {
1134: Clipboard clip = getSystemSelection();
1135:
1136: if (clip != null) {
1137: String rangeText = component.getWebForm()
1138: .getDomDocument().getRangeText(range);
1139:
1140: clip.setContents(new StringSelection(rangeText),
1141: getClipboardOwner());
1142: ownsSelection = true;
1143: }
1144: }
1145: }
1146:
1147: private Clipboard getSystemSelection() {
1148: try {
1149: return component.getToolkit().getSystemSelection();
1150: } catch (HeadlessException he) {
1151: ErrorManager.getDefault().notify(
1152: ErrorManager.INFORMATIONAL, he);
1153:
1154: // do nothing... there is no system clipboard
1155: }
1156:
1157: return null;
1158: }
1159:
1160: private ClipboardOwner getClipboardOwner() {
1161: return handler;
1162: }
1163:
1164: /**
1165: * Saves the current caret position. This is used when caret up/down actions occur, moving
1166: * between lines that have uneven end positions.
1167: *
1168: * @param p
1169: * the position
1170: * @see #getMagicCaretPosition
1171: */
1172: public void setMagicCaretPosition(Point p) {
1173: magicCaretPosition = p;
1174: }
1175:
1176: /**
1177: * Gets the saved caret position.
1178: *
1179: * @return the position see #setMagicCaretPosition
1180: */
1181: public Point getMagicCaretPosition() {
1182: return magicCaretPosition;
1183: }
1184:
1185: /** Notify caret that the range it is attached to is no longer valid */
1186: public void detachDom() {
1187: if (range != null) {
1188: range.detach();
1189: range = null;
1190: }
1191: }
1192:
1193: /**
1194: * Compares this object to the specified object. The superclass behavior of comparing rectangles
1195: * is not desired, so this is changed to the Object behavior.
1196: *
1197: * @param obj
1198: * the object to compare this font with
1199: * @return <code>true</code> if the objects are equal; <code>false</code> otherwise
1200: */
1201: public boolean equals(Object obj) {
1202: return (this == obj);
1203: }
1204:
1205: // XXX This class should override hashCode too! I recently alerted the Swing group to this too.
1206: public String toString() {
1207: if (range != null) {
1208: return range.toString();
1209: } else {
1210: return "Caret-range is nul";
1211: }
1212: }
1213:
1214: // Rectangle.contains returns false if passed a rect with a w or h == 0,
1215: // this won't (assuming X,Y are contained with this rectangle).
1216: private boolean _contains(int X, int Y, int W, int H) {
1217: int w = this .width;
1218: int h = this .height;
1219:
1220: if ((w | h | W | H) < 0) {
1221: // At least one of the dimensions is negative...
1222: return false;
1223: }
1224:
1225: // Note: if any dimension is zero, tests below must return false...
1226: int x = this .x;
1227: int y = this .y;
1228:
1229: if ((X < x) || (Y < y)) {
1230: return false;
1231: }
1232:
1233: if (W > 0) {
1234: w += x;
1235: W += X;
1236:
1237: if (W <= X) {
1238: // X+W overflowed or W was zero, return false if...
1239: // either original w or W was zero or
1240: // x+w did not overflow or
1241: // the overflowed x+w is smaller than the overflowed X+W
1242: if ((w >= x) || (W > w)) {
1243: return false;
1244: }
1245: } else {
1246: // X+W did not overflow and W was not zero, return false if...
1247: // original w was zero or
1248: // x+w did not overflow and x+w is smaller than X+W
1249: if ((w >= x) && (W > w)) {
1250: return false;
1251: }
1252: }
1253: } else if ((x + w) < X) {
1254: return false;
1255: }
1256:
1257: if (H > 0) {
1258: h += y;
1259: H += Y;
1260:
1261: if (H <= Y) {
1262: if ((h >= y) || (H > h)) {
1263: return false;
1264: }
1265: } else {
1266: if ((h >= y) && (H > h)) {
1267: return false;
1268: }
1269: }
1270: } else if ((y + h) < Y) {
1271: return false;
1272: }
1273:
1274: return true;
1275: }
1276:
1277: /*private*/void removeSelection() {
1278: // assert range != null;
1279: if (range == null) {
1280: ErrorManager.getDefault().notify(
1281: ErrorManager.INFORMATIONAL,
1282: new IllegalStateException("Range is null.")); // NOI18N
1283: return;
1284: }
1285: // range.deleteContents();
1286: boolean success = component.getWebForm().getDomDocument()
1287: .deleteRangeContents(range);
1288: if (!success) {
1289: // Read-only - can't edit!!
1290: // UIManager.getLookAndFeel().provideErrorFeedback(webform.getPane());
1291: UIManager.getLookAndFeel().provideErrorFeedback(component);
1292: }
1293: }
1294:
1295: String getSelectedText() {
1296: if (range == null) {
1297: ErrorManager.getDefault().notify(
1298: ErrorManager.INFORMATIONAL,
1299: new IllegalStateException("Range is null.")); // NOI18N
1300: return ""; // NOI18N
1301: }
1302: // return range.getText();
1303: if (component == null) {
1304: ErrorManager.getDefault().notify(
1305: ErrorManager.INFORMATIONAL,
1306: new IllegalStateException("Component is null.")); // NOI18N
1307: return ""; // NOI18N
1308: }
1309: return component.getWebForm().getDomDocument().getRangeText(
1310: range);
1311: }
1312:
1313: /**
1314: * Return true iff we have a selection - or put differently, if the dot is at a different
1315: * location than the mark.
1316: */
1317: public boolean hasSelection() {
1318: return (range != null) && !range.isEmpty();
1319: }
1320:
1321: // XXX Moved to DesignerPaneBase.
1322: // /** Return the text in the selection, if any (if not returns null).
1323: // * If the cut parameter is true, then the selection is deleted too.
1324: // */
1325: // public Transferable copySelection(boolean cut) {
1326: // if (hasSelection()) {
1327: // String text = range.getText();
1328: // assert text.length() > 0;
1329: //
1330: // Transferable transferable = new StringSelection(text);
1331: //
1332: // if (cut) {
1333: // removeSelection();
1334: // }
1335: //
1336: // return transferable;
1337: // } else {
1338: // return new StringSelection("");
1339: // }
1340: // }
1341:
1342: // XXX Moved to DesignerPaneBase.
1343: // /**
1344: // * Replace the selection. Beep if within a read-only region.
1345: // */
1346: // public void replaceSelection(String content) {
1347: //// WebForm webform = component.getDocument().getWebForm();
1348: // WebForm webform = component.getWebForm();
1349: //
1350: // InlineEditor editor = webform.getManager().getInlineEditor();
1351: //
1352: // if ((content.equals("\n") || content.equals("\r\n")) && // NOI18N
1353: // (editor != null) && !editor.isMultiLine()) {
1354: // // Commit
1355: // // Should I look to see if the Shift key is pressed, and if so let
1356: // // you insert a newline?
1357: // webform.getManager().finishInlineEditing(false);
1358: //
1359: // return;
1360: // }
1361: //
1362: // /*
1363: // if (range.isReadOnlyRegion()) {
1364: // UIManager.getLookAndFeel().provideErrorFeedback(component);
1365: // return;
1366: // }
1367: // */
1368: // if (hasSelection()) {
1369: // removeSelection();
1370: // }
1371: //
1372: //// Position pos = getDot();
1373: // DomPosition pos = getDot();
1374: //
1375: // if (editor == null) {
1376: //// assert (pos == Position.NONE) || !pos.isRendered();
1377: //// if (pos != Position.NONE && MarkupService.isRenderedNode(pos.getNode())) {
1378: // if (pos != DomPosition.NONE && MarkupService.isRenderedNode(pos.getNode())) {
1379: // ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL,
1380: // new IllegalStateException("Node is expected to be not rendered, node=" + pos.getNode())); // NOI18N
1381: // }
1382: // } // else: Stay in the DocumentFragment; don't jump to the source DOM (there is none)
1383: //
1384: //// if (pos == Position.NONE) {
1385: // if (pos == DomPosition.NONE) {
1386: // UIManager.getLookAndFeel().provideErrorFeedback(component);
1387: //
1388: // return;
1389: // }
1390: //
1391: //// component.getDocument().insertString(this, pos, content);
1392: //// component.getDocument().insertString(pos, content);
1393: // component.getWebForm().getDomDocument().insertString(pos, content);
1394: // }
1395:
1396: // XXX Moved to designer/jsf/../DomDocumentIpmpl.
1397: /**
1398: * @todo Check deletion back to first char in <body> !
1399: * @todo Refactor document mutation into the document class
1400: * @todo Check read-only state etc
1401: */
1402: public boolean removeNextChar() {
1403: // TODO - compute previous visual position, decide if it's
1404: // // isWithinEditableRegion(Position pos)
1405: // // and if so, set the range to it and delete the range.
1406: // if (hasSelection()) {
1407: // removeSelection();
1408: //
1409: // return true;
1410: // }
1411: //
1412: //// Document doc = component.getDocument();
1413: //// Position mark = range.getMark();
1414: // DomPosition mark = range.getMark();
1415: //// Position dot = ModelViewMapper.computeArrowRight(doc.getWebForm(), mark);
1416: //// Position dot = ModelViewMapper.computeArrowRight(component.getWebForm(), mark);
1417: // DomPosition dot = ModelViewMapper.computeArrowRight(component.getWebForm(), mark);
1418: //
1419: //// if ((dot == Position.NONE) || !isWithinEditableRegion(dot)) {
1420: // if ((dot == DomPosition.NONE) || !isWithinEditableRegion(dot)) {
1421: // UIManager.getLookAndFeel().provideErrorFeedback(component); // beep
1422: //
1423: // return false;
1424: // }
1425: //
1426: // range.setRange(dot.getNode(), dot.getOffset(), mark.getNode(), mark.getOffset());
1427: //// range.deleteContents();
1428: // removeSelection();
1429: //
1430: // return true;
1431: WebForm webForm = component.getWebForm();
1432: return webForm.getDomDocument().deleteNextChar(webForm, range);
1433: }
1434:
1435: /**
1436: * @todo Check deletion back to first char in <body> !
1437: * @todo Refactor document mutation into the document class
1438: * @todo Check read-only state etc
1439: */
1440: public boolean removePreviousChar() {
1441: // TODO - compute previous visual position, decide if it's
1442: // // isWithinEditableRegion(Position pos)
1443: // // and if so, set the range to it and delete the range.
1444: // if (hasSelection()) {
1445: // removeSelection();
1446: //
1447: // return true;
1448: // }
1449: //
1450: //// Document doc = component.getDocument();
1451: //// Position mark = range.getMark();
1452: // DomPosition mark = range.getMark();
1453: //// Position dot = ModelViewMapper.computeArrowLeft(doc.getWebForm(), mark);
1454: //// Position dot = ModelViewMapper.computeArrowLeft(component.getWebForm(), mark);
1455: // DomPosition dot = ModelViewMapper.computeArrowLeft(component.getWebForm(), mark);
1456: //
1457: //// if ((dot == Position.NONE) || !isWithinEditableRegion(dot)) {
1458: // if ((dot == DomPosition.NONE) || !isWithinEditableRegion(dot)) {
1459: // UIManager.getLookAndFeel().provideErrorFeedback(component); // beep
1460: //
1461: // return false;
1462: // }
1463: //
1464: // range.setRange(dot.getNode(), dot.getOffset(), mark.getNode(), mark.getOffset());
1465: //
1466: // // XXX DEBUGGING ONLY
1467: // /*
1468: // Element element = doc.getBody();
1469: // if (element != null) {
1470: // System.out.println("BEFORE DELETION: " + org.netbeans.modules.visualweb.css2.FacesSupport.getHtmlStream(element));
1471: // }
1472: // */
1473: //// range.deleteContents();
1474: // removeSelection();
1475: //
1476: // // XXX DEBUGGING ONLY
1477: //
1478: // /*
1479: // if (element != null) {
1480: // System.out.println("BEFORE DELETION: " + org.netbeans.modules.visualweb.css2.FacesSupport.getHtmlStream(element));
1481: // }
1482: // */
1483: // return true;
1484: WebForm webForm = component.getWebForm();
1485: return webForm.getDomDocument().deletePreviousChar(webForm,
1486: range);
1487: }
1488:
1489: boolean replaceSelection(String content) {
1490: // XXX Moved to DomDocumentImpl.
1491: /*
1492: if (range.isReadOnlyRegion()) {
1493: UIManager.getLookAndFeel().provideErrorFeedback(component);
1494: return;
1495: }
1496: */
1497: // if (hasSelection()) {
1498: // removeSelection();
1499: // }
1500: //
1501: //// Position pos = getDot();
1502: // DomPosition pos = getDot();
1503: //
1504: //// if (editor == null) {
1505: // if (!component.getWebForm().isInlineEditing()) {
1506: //// assert (pos == Position.NONE) || !pos.isRendered();
1507: //// if (pos != Position.NONE && MarkupService.isRenderedNode(pos.getNode())) {
1508: // if (pos != DomPosition.NONE && MarkupService.isRenderedNode(pos.getNode())) {
1509: // ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL,
1510: // new IllegalStateException("Node is expected to be not rendered, node=" + pos.getNode())); // NOI18N
1511: // return false;
1512: // }
1513: // } // else: Stay in the DocumentFragment; don't jump to the source DOM (there is none)
1514: //
1515: //// if (pos == Position.NONE) {
1516: // if (pos == DomPosition.NONE) {
1517: //// UIManager.getLookAndFeel().provideErrorFeedback(this);
1518: // return false;
1519: // }
1520: //
1521: //// component.getDocument().insertString(this, pos, content);
1522: //// component.getDocument().insertString(pos, content);
1523: // return component.getWebForm().getDomDocument().insertString(pos, content);
1524: return component.getWebForm().getDomDocument().insertString(
1525: component.getWebForm(), range, content);
1526: }
1527:
1528: // XXX Moved to WebForm.
1529: // /** Return true iff the position is within the editable portion of the document. */
1530: //// public boolean isWithinEditableRegion(Position pos) {
1531: // public boolean isWithinEditableRegion(DomPosition pos) {
1532: //// WebForm webform = component.getDocument().getWebForm();
1533: // WebForm webform = component.getWebForm();
1534: //
1535: // InlineEditor editor = webform.getManager().getInlineEditor();
1536: //
1537: // if (editor != null) {
1538: //// Position editableRegionStart = editor.getBegin();
1539: //// Position editableRegionEnd = editor.getEnd();
1540: // DomPosition editableRegionStart = editor.getBegin();
1541: // DomPosition editableRegionEnd = editor.getEnd();
1542: //
1543: //// assert editableRegionStart != Position.NONE;
1544: //// assert editableRegionEnd != Position.NONE;
1545: //
1546: // return pos.isLaterThan(editableRegionStart) && pos.isEarlierThan(editableRegionEnd);
1547: // }
1548: //
1549: //// assert !pos.isRendered() : pos;
1550: // if (MarkupService.isRenderedNode(pos.getNode())) {
1551: // ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL,
1552: // new IllegalStateException("Node is expected to be not rendered, node=" + pos.getNode()));
1553: // }
1554: //
1555: //
1556: // if (!webform.isGridMode()) {
1557: // // In page flow mode, all regions are editable. Note - this
1558: // // may not be true when I start allowing sub-grids
1559: // return true;
1560: // }
1561: //
1562: // CssBox box = webform.getManager().getInsertModeBox();
1563: //
1564: // if (box == null) {
1565: // return false;
1566: // }
1567: //
1568: // //Position editableRegionStart = Position.create(box.getSourceElement());
1569: // //Position editableRegionEnd = new Position(null, editableRegionStart.getNode(),
1570: // // editableRegionStart.getOffset()+1);
1571: // Element componentRootElement = CssBox.getElementForComponentRootCssBox(box);
1572: //// Position editableRegionStart =
1573: //// new Position(box.getDesignBean().getElement(), 0, Bias.FORWARD);
1574: // // XXX Possible NPE?
1575: //// new Position(CssBox.getMarkupDesignBeanForCssBox(box).getElement(), 0, Bias.FORWARD);
1576: //// new Position(WebForm.getDomProviderService().getSourceElement(componentRootElement), 0, Bias.FORWARD);
1577: //// DomPosition editableRegionStart = DesignerPaneBase.createDomPosition(WebForm.getDomProviderService().getSourceElement(componentRootElement), 0, Bias.FORWARD);
1578: // DomPosition editableRegionStart = component.getWebForm().createDomPosition(WebForm.getDomProviderService().getSourceElement(componentRootElement), 0, Bias.FORWARD);
1579: //
1580: //// Position editableRegionEnd =
1581: //// new Position(editableRegionStart.getNode(),
1582: //// editableRegionStart.getNode().getChildNodes().getLength(), Bias.BACKWARD);
1583: //// DomPosition editableRegionEnd = DesignerPaneBase.createDomPosition(editableRegionStart.getNode(), editableRegionStart.getNode().getChildNodes().getLength(), Bias.BACKWARD);
1584: // DomPosition editableRegionEnd = component.getWebForm().createDomPosition(editableRegionStart.getNode(), editableRegionStart.getNode().getChildNodes().getLength(), Bias.BACKWARD);
1585: //
1586: // return pos.isLaterThan(editableRegionStart) && pos.isEarlierThan(editableRegionEnd);
1587: // }
1588:
1589: /** Return true iff the caret is within a read only region */
1590: public boolean isReadOnlyRegion() {
1591: return (range != null) ? range.isReadOnlyRegion() : false;
1592: }
1593:
1594: class SafeScroller implements Runnable {
1595: Rectangle r;
1596:
1597: SafeScroller(Rectangle r) {
1598: this .r = r;
1599: }
1600:
1601: public void run() {
1602: if (component != null) {
1603: component.scrollRectToVisible(r);
1604: }
1605: }
1606: }
1607:
1608: class Handler implements ActionListener, ClipboardOwner,
1609: DomDocumentListener {
1610: // --- ActionListener methods ----------------------------------
1611:
1612: /**
1613: * Invoked when the blink timer fires. This is called asynchronously. The simply changes the
1614: * visibility and repaints the rectangle that last bounded the caret.
1615: *
1616: * @param e
1617: * the action event
1618: */
1619: public void actionPerformed(ActionEvent e) {
1620: if (((width == 0) || (height == 0)) && isVisible()) {
1621: // setVisible(true) will cause a scroll, only do this if the
1622: // new location is really valid.
1623: if (component != null) {
1624: DesignerPaneBaseUI mapper = component.getUI();
1625:
1626: if (range != null) {
1627: // Position dot = range.getDot();
1628: DomPosition dot = range.getDot();
1629: // Rectangle r = mapper.modelToView(/*component,*/ dot);
1630: // WebForm webForm = component.getDocument().getWebForm();
1631: WebForm webForm = component.getWebForm();
1632:
1633: Rectangle r = webForm.modelToView(dot);
1634:
1635: if ((r != null) && (r.width != 0)
1636: && (r.height != 0)) {
1637: damage(r);
1638: }
1639: }
1640: }
1641: }
1642:
1643: flashOn = !flashOn;
1644: repaint();
1645: }
1646:
1647: //
1648: // ClipboardOwner
1649: //
1650:
1651: /**
1652: * Toggles the visibility of the selection when ownership is lost.
1653: */
1654: public void lostOwnership(Clipboard clipboard,
1655: Transferable contents) {
1656: if (ownsSelection) {
1657: ownsSelection = false;
1658:
1659: if ((component != null) && !component.hasFocus()) {
1660: setSelectionVisible(false);
1661: }
1662: }
1663: }
1664:
1665: //////////////////////////
1666: // DomDocumentListener >>>
1667:
1668: public void insertUpdate(DomDocumentEvent evt) {
1669: DesignerCaret.this .setDot(evt.getDomPosition());
1670: }
1671:
1672: public void componentMoved(DomDocumentEvent evt) {
1673: // XXX 126234 Possible NPE.
1674: if (component == null) {
1675: return;
1676: }
1677:
1678: if (component.hasCaret()) {
1679: DomPosition pos = ModelViewMapper
1680: .getFirstDocumentPosition(component
1681: .getWebForm(), false);
1682: // webform.getPane().getCaret().setDot(pos);
1683: // webForm.getPane().setCaretDot(pos);
1684: setDot(pos);
1685: }
1686: }
1687:
1688: public void componentsMoved(DomDocumentEvent evt) {
1689: // XXX #91531 User didn't want to have this kind of autoscroll behavior.
1690: // final Rectangle rect = boundingBox;
1691: // // #6331237 NPE.
1692: // if(rect != null) {
1693: // SwingUtilities.invokeLater(new Runnable() {
1694: // public void run() {
1695: // editor.scrollRectToVisible(rect);
1696: // }
1697: // });
1698: // }
1699: }
1700:
1701: public void componentMovedTo(DomDocumentEvent evt) {
1702: // XXX For not nothing here.
1703: }
1704: // DomDocumentListener <<<
1705: //////////////////////////
1706: }
1707: }
|