📄 configurablecaret.java~1~
字号:
// Make sure the caret is visible if this window has the focus. if (flasher != null && flasher.isRunning()) { visible = true; flasher.restart(); } repaint(); }/*****************************************************************************/ /** * Sets the caret visibility, and repaints the caret. * It is important to understand the relationship between this method, * <code>isVisible</code> and <code>isActive</code>. * Calling this method with a value of <code>true</code> activates the * caret blinking. Setting it to <code>false</code> turns it completely off. * To determine whether the blinking is active, you should call * <code>isActive</code>. In effect, <code>isActive</code> is an * appropriate corresponding "getter" method for this one. * <code>isVisible</code> can be used to fetch the current * visibility status of the caret, meaning whether or not it is currently * painted. This status will change as the caret blinks on and off. * <p> * Here's a list showing the potential return values of both * <code>isActive</code> and <code>isVisible</code> * after calling this method: * <p> * <b><code>setVisible(true)</code></b>: * <ul> * <li>isActive(): true</li> * <li>isVisible(): true or false depending on whether * or not the caret is blinked on or off</li> * </ul> * <p> * <b><code>setVisible(false)</code></b>: * <ul> * <li>isActive(): false</li> * <li>isVisible(): false</li> * </ul> * * @param e the visibility specifier * @see #isActive * @see Caret#setVisible */ public void setVisible(boolean e) { // focus lost notification can come in later after the // caret has been deinstalled, in which case the component // will be null. if (component != null) { active = e; TextUI mapper = component.getUI(); if (visible != e) { visible = e; // repaint the caret try { Rectangle loc = mapper.modelToView(component, dot); validateWidth(loc); damage(loc); } catch (BadLocationException badloc) { // hmm... not legally positioned } } } // End of if (component != null). if (flasher != null) { if (visible) flasher.start(); else flasher.stop(); } }/*****************************************************************************/ public String toString() { String s = "Dot=" + dot; s += " Mark=" + mark; return s; }/*****************************************************************************/ private void updateSystemSelection() { if (this.dot!=this.mark && component!=null) { Clipboard clip = getSystemSelection(); if (clip != null) { String selectedText = null; selectedText = component.getSelectedText(); clip.setContents(new StringSelection(selectedText), getClipboardOwner()); ownsSelection = true; } } }/*****************************************************************************/ /** * Helper function used by the block and underline carets to * ensure the width of the painted caret is valid. This is * done for the following reasons: * * <ul> * <li>The <code>View</code> classes in the javax.swing.text * package always return a width of "1" when * <code>modelToView</code> is called. We'll be needing * the actual width.</li> * <li>Even in smart views, such as * <code>RSyntaxTextArea</code>'s <code>SyntaxView</code> * and <code>WrappedSyntaxView</code> that return the * width of the current character, if the caret is at * the end of a line for example, the width returned * from <code>modelToView</code> will be 0 (as the width * of unprintable characters such as '\n' is calculated * as 0). In this case, we'll use a default width * value.</li> * </ul> * * @param rect The rectangle returned by the current * <code>View</code>'s <code>modelToView</code> * method for the caret position. */ private final void validateWidth(Rectangle rect) { // If the width value > 1, we assume the View is // a "smart" view that returned the proper width. // So only worry about this stuff if width <= 1. if (rect!=null && rect.width<=1) { // The width is either 1 (most likely, we're // using a "dumb" view like those in // javax.swing.text) or 0 (most likely, // we're using a "smart" view like // org.fife.ui.rsyntaxtextarea.SyntaxView, // we're at the end of a line, and the // width of '\n' is being computed as 0). try { // Try to get a width for the character // at the caret position. We use the text // area's font instead of g's because // g's may vary in an RSyntaxTextArea. component.getDocument().getText(dot,1, seg); Font font = component.getFont(); FontMetrics fm = component. getFontMetrics(font); rect.width = fm.charWidth( seg.array[seg.offset]); // This width being returned 0 likely means // that it is an unprintable character (which // is almost 100% to be a newline char, i.e., // we're at the end of a line). So, just use // the width of a space. if (rect.width==0) { rect.width = fm.charWidth(' '); } } catch (BadLocationException ble) { // This shouldn't ever happen. ble.printStackTrace(); rect.width = 8; } } // End of if (rect.width<=1). }/*****************************************************************************/ private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); s.writeInt(getStyle()); }/*****************************************************************************//*********************** PRIVATE INNER CLASSES *******************************//*****************************************************************************/ /** * Scrolls the text component that contains this caret so that this * caret is visible. This operation is wrapped in a * <code>Runnable</code> so that it is threadsafe. */ class SafeScroller implements Runnable { private Rectangle r; SafeScroller(Rectangle r) { this.r = r; } public void run() { if (component != null) component.scrollRectToVisible(r); } }/*****************************************************************************/ /** * Handles many events from this caret. */ class Handler implements PropertyChangeListener, DocumentListener, ActionListener, ClipboardOwner { /** * Invoked when the blink timer fires. This is called * asynchronously. The simply changes the visibility * and repaints the rectangle that last bounded the caret. * * @param e the action event */ public void actionPerformed(ActionEvent e) { if (width == 0 || height == 0) { // setVisible(true) will cause a scroll, only do this if the // new location is really valid. if (component != null) { TextUI mapper = component.getUI(); try { Rectangle r = mapper.modelToView(component, dot); if (r!=null && r.width!=0 && r.height!=0) { validateWidth(r); damage(r); } } catch (BadLocationException ble) { } } } visible = !visible; repaint(); } /** * Updates the dot and mark if they were changed by * the insertion. * * @param e the document event * @see DocumentListener#insertUpdate */ public void insertUpdate(DocumentEvent e) { if (!SwingUtilities.isEventDispatchThread()) { if ((e.getOffset() <= dot || e.getOffset() <= mark) && selectionTag != null) { try { component.getHighlighter().changeHighlight(selectionTag, Math.min(dot, mark), Math.max(dot, mark)); } catch (BadLocationException e1) { e1.printStackTrace(); } } return; } int adjust = 0; int offset = e.getOffset(); int length = e.getLength(); int newDot = dot; short changed = 0; if (component.inUndoRedo()) { setDot(offset + length); return; } if (newDot >= offset) { newDot += length; changed |= 1; } int newMark = mark; if (newMark >= offset) { newMark += length; changed |= 2; } if (changed != 0) { if (newMark == newDot) { setDot(newDot); ensureValidPosition(); } else { setDot(newMark); if (getDot() == newMark) { // Due this test in case the filter vetoed the // change in which case this probably won't be // valid either. moveDot(newDot); } ensureValidPosition(); } } } /** * Updates the dot and mark if they were changed * by the removal. * * @param e the document event * @see DocumentListener#removeUpdate */ public void removeUpdate(DocumentEvent e) { if (!SwingUtilities.isEventDispatchThread()) { int length = component.getDocument().getLength(); dot = Math.min(dot, length); mark = Math.min(mark, length); if ((e.getOffset() < dot || e.getOffset() < mark) && selectionTag != null) { try { component.getHighlighter().changeHighlight(selectionTag, Math.min(dot, mark), Math.max(dot, mark)); } catch (BadLocationException e1) { e1.printStackTrace(); } } return; } int offs0 = e.getOffset(); int offs1 = offs0 + e.getLength(); int adjust = 0; int newDot = dot; int newMark = mark; if(component.inUndoRedo()) { setDot(offs0); return; } if (newDot >= offs1) newDot -= (offs1 - offs0); else if (newDot >= offs0) newDot = offs0; if (newMark >= offs1) newMark -= (offs1 - offs0); else if (newMark >= offs0) newMark = offs0; if (newMark == newDot) { forceCaretPositionChange = true; try { setDot(newDot); } finally { forceCaretPositionChange = false; } ensureValidPosition(); } else { setDot(newMark); if (getDot() == newMark) { // Due this test in case the filter vetoed the change // in which case this probably won't be valid either. moveDot(newDot); } ensureValidPosition(); } } /** * Gives notification that an attribute or set of attributes changed. * * @param e the document event * @see DocumentListener#changedUpdate */ public void changedUpdate(DocumentEvent e) { if (!SwingUtilities.isEventDispatchThread()) return; if(component.inUndoRedo()) setDot(e.getOffset() + e.getLength()); } /** * This method gets called when a bound property is changed. * We are looking for document changes on the editor. */ public void propertyChange(PropertyChangeEvent evt) { Object oldValue = evt.getOldValue(); Object newValue = evt.getNewValue(); if ((oldValue instanceof Document) || (newValue instanceof Document)) { setDot(0); if (oldValue != null) ((Document)oldValue).removeDocumentListener(this); if (newValue != null) ((Document)newValue).addDocumentListener(this); } else if("enabled".equals(evt.getPropertyName())) { Boolean enabled = (Boolean) evt.getNewValue(); if(component.isFocusOwner()) { if(enabled == Boolean.TRUE) { if(component.isEditable()) setVisible(true); setSelectionVisible(true); } else { setVisible(false); setSelectionVisible(false); } } } } /** * Toggles the visibility of the selection when ownership is lost. */ public void lostOwnership(Clipboard clipboard, Transferable contents) { if (ownsSelection) { ownsSelection = false; if (component != null && !component.hasFocus()) setSelectionVisible(false); } } }/*****************************************************************************/ /** * Filter bypass for this caret class. */ private class DefaultFilterBypass extends NavigationFilter.FilterBypass { public Caret getCaret() { return ConfigurableCaret.this; } public void setDot(int dot, Position.Bias bias) { handleSetDot(dot); } public void moveDot(int dot, Position.Bias bias) { handleMoveDot(dot); } }/*****************************************************************************/}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -