/** * Returns the end of the word at the given offset. * * @param textArea The text area. * @param offs The offset into the text area's content. * @return The end offset of the word. * @throws BadLocationException If <code>offs</code> is invalid. * @see #getWordStart(RSyntaxTextArea, int) */ public static int getWordEnd(RSyntaxTextArea textArea, int offs) throws BadLocationException { Document doc = textArea.getDocument(); int endOffs = textArea.getLineEndOffsetOfCurrentLine(); int lineEnd = Math.min(endOffs, doc.getLength()); if (offs == lineEnd) { // End of the line. return offs; } String s = doc.getText(offs, lineEnd - offs - 1); if (s != null && s.length() > 0) { // Should always be true int i = 0; int count = s.length(); char ch = s.charAt(i); if (Character.isWhitespace(ch)) { while (i < count && Character.isWhitespace(s.charAt(i++))) ; } else if (Character.isLetterOrDigit(ch)) { while (i < count && Character.isLetterOrDigit(s.charAt(i++))) ; } else { i = 2; } offs += i - 1; } return offs; }
public StringBuilder appendHTMLRepresentation( StringBuilder sb, RSyntaxTextArea textArea, boolean fontFamily, boolean tabsToSpaces) { SyntaxScheme colorScheme = textArea.getSyntaxScheme(); Style scheme = colorScheme.getStyle(getType()); Font font = textArea.getFontForTokenType(getType()); // scheme.font; if (font.isBold()) sb.append("<b>"); if (font.isItalic()) sb.append("<em>"); if (scheme.underline || isHyperlink()) sb.append("<u>"); sb.append("<font"); if (fontFamily) { sb.append(" face=\"").append(font.getFamily()).append("\""); } sb.append(" color=\"").append(getHTMLFormatForColor(scheme.foreground)).append("\">"); // NOTE: Don't use getLexeme().trim() because whitespace tokens will // be turned into NOTHING. appendHtmlLexeme(textArea, sb, tabsToSpaces); sb.append("</font>"); if (scheme.underline || isHyperlink()) sb.append("</u>"); if (font.isItalic()) sb.append("</em>"); if (font.isBold()) sb.append("</b>"); return sb; }
@Override public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) { int offs = -1; if (!isAllocationValid()) { Rectangle alloc = a.getBounds(); setSize(alloc.width, alloc.height); } // Get the child view for the line at (x,y), and ask it for the // specific offset. Rectangle alloc = getInsideAllocation(a); View v = getViewAtPoint((int) x, (int) y, alloc); if (v != null) { offs = v.viewToModel(x, y, alloc, bias); } // Code folding may have hidden the last line. If so, return the last // visible offset instead of the last offset. if (host.isCodeFoldingEnabled() && v == getView(getViewCount() - 1) && offs == v.getEndOffset() - 1) { offs = host.getLastVisibleOffset(); } return offs; }
/** * Determines the preferred span for this view along an axis. * * @param axis may be either View.X_AXIS or View.Y_AXIS * @return the span the view would like to be rendered into >= 0. Typically the view is told to * render into the span that is returned, although there is no guarantee. The parent may * choose to resize or break the view. * @exception IllegalArgumentException for an invalid axis */ @Override public float getPreferredSpan(int axis) { updateMetrics(); switch (axis) { case View.X_AXIS: float span = longLineWidth + getRhsCorrection(); // fudge factor if (host.getEOLMarkersVisible()) { span += metrics.charWidth('\u00B6'); } return span; case View.Y_AXIS: // We update lineHeight here as when this method is first // called, lineHeight isn't initialized. If we don't do it // here, we get no vertical scrollbar (as lineHeight==0). lineHeight = host != null ? host.getLineHeight() : lineHeight; // return getElement().getElementCount() * lineHeight; int visibleLineCount = getElement().getElementCount(); if (host.isCodeFoldingEnabled()) { visibleLineCount -= host.getFoldManager().getHiddenLineCount(); } return visibleLineCount * (float) lineHeight; default: throw new IllegalArgumentException("Invalid axis: " + axis); } }
/** * Returns whether the MLC token containing <code>offs</code> appears to have a "nested" comment * (i.e., contains "<code>/*</code>" somewhere inside of it). This implies that it is likely a * "new" MLC and needs to be closed. While not foolproof, this is usually good enough of a sign. * * @param textArea * @param line * @param offs * @return Whether a comment appears to be nested inside this one. */ private boolean appearsNested(RSyntaxTextArea textArea, int line, int offs) { final int firstLine = line; // Remember the line we start at. while (line < textArea.getLineCount()) { Token t = textArea.getTokenListForLine(line); int i = 0; // If examining the first line, start at offs. if (line++ == firstLine) { t = RSyntaxUtilities.getTokenAtOffset(t, offs); if (t == null) { // offs was at end of the line continue; } i = t.documentToToken(offs); } else { i = t.getTextOffset(); } int textOffset = t.getTextOffset(); while (i < textOffset + t.length() - 1) { if (t.charAt(i - textOffset) == '/' && t.charAt(i - textOffset + 1) == '*') { return true; } i++; } // If tokens come after this one on this line, our MLC ended. if (t.getNextToken() != null) { return false; } } return true; // No match - MLC goes to the end of the file }
/** * Provides a mapping from the view coordinate space to the logical coordinate space of the model. * * @param fx the X coordinate >= 0 * @param fy the Y coordinate >= 0 * @param a the allocated region to render into * @return the location within the model that best represents the given point in the view >= 0 */ @Override public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) { bias[0] = Position.Bias.Forward; Rectangle alloc = a.getBounds(); RSyntaxDocument doc = (RSyntaxDocument) getDocument(); int x = (int) fx; int y = (int) fy; // If they're asking about a view position above the area covered by // this view, then the position is assumed to be the starting position // of this view. if (y < alloc.y) { return getStartOffset(); } // If they're asking about a position below this view, the position // is assumed to be the ending position of this view. else if (y > alloc.y + alloc.height) { return host.getLastVisibleOffset(); } // They're asking about a position within the coverage of this view // vertically. So, we figure out which line the point corresponds to. // If the line is greater than the number of lines contained, then // simply use the last line as it represents the last possible place // we can position to. else { Element map = doc.getDefaultRootElement(); int lineIndex = Math.abs((y - alloc.y) / lineHeight); // metrics.getHeight() ); FoldManager fm = host.getFoldManager(); // System.out.print("--- " + lineIndex); lineIndex += fm.getHiddenLineCountAbove(lineIndex, true); // System.out.println(" => " + lineIndex); if (lineIndex >= map.getElementCount()) { return host.getLastVisibleOffset(); } Element line = map.getElement(lineIndex); // If the point is to the left of the line... if (x < alloc.x) { return line.getStartOffset(); } else if (x > alloc.x + alloc.width) { return line.getEndOffset() - 1; } else { // Determine the offset into the text int p0 = line.getStartOffset(); Token tokenList = doc.getTokenListForLine(lineIndex); tabBase = alloc.x; int offs = tokenList.getListOffset((RSyntaxTextArea) getContainer(), this, tabBase, x); return offs != -1 ? offs : p0; } } // End of else. }
/** * Provides a mapping from the document model coordinate space to the coordinate space of the * view mapped to it. * * @param pos the position to convert * @param a the allocated region to render into * @return the bounding box of the given position is returned * @exception BadLocationException if the given position does not represent a valid location in * the associated document. */ @Override public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { // System.err.println("--- begin modelToView ---"); Rectangle alloc = a.getBounds(); RSyntaxTextArea textArea = (RSyntaxTextArea) getContainer(); alloc.height = textArea.getLineHeight(); // metrics.getHeight(); alloc.width = 1; int p0 = getStartOffset(); int p1 = getEndOffset(); int testP = (b == Position.Bias.Forward) ? pos : Math.max(p0, pos - 1); // Get the token list for this line so we don't have to keep // recomputing it if this logical line spans multiple physical // lines. RSyntaxDocument doc = (RSyntaxDocument) getDocument(); Element map = doc.getDefaultRootElement(); int line = map.getElementIndex(p0); Token tokenList = doc.getTokenListForLine(line); float x0 = alloc.x; // 0; while (p0 < p1) { TokenSubList subList = TokenUtils.getSubTokenList( tokenList, p0, WrappedSyntaxView.this, textArea, x0, lineCountTempToken); x0 = subList != null ? subList.x : x0; tokenList = subList != null ? subList.tokenList : null; int p = calculateBreakPosition(p0, tokenList, x0); if ((pos >= p0) && (testP < p)) { // pos < p)) { // it's in this line alloc = RSyntaxUtilities.getLineWidthUpTo( textArea, s, p0, pos, WrappedSyntaxView.this, alloc, alloc.x); // System.err.println("--- end modelToView ---"); return alloc; } // if (p == p1 && pos == p1) { if (p == p1 - 1 && pos == p1 - 1) { // Wants end. if (pos > p0) { alloc = RSyntaxUtilities.getLineWidthUpTo( textArea, s, p0, pos, WrappedSyntaxView.this, alloc, alloc.x); } // System.err.println("--- end modelToView ---"); return alloc; } p0 = (p == p0) ? p1 : p; // System.err.println("... ... Incrementing y"); alloc.y += alloc.height; } throw new BadLocationException(null, pos); }
/** * Returns the template that should be inserted at the current caret position, assuming the * trigger character was pressed. * * @param textArea The text area that's getting text inserted into it. * @return A template that should be inserted, if appropriate, or <code>null</code> if no template * should be inserted. */ public CodeTemplate getTemplate(RSyntaxTextArea textArea) { int caretPos = textArea.getCaretPosition(); int charsToGet = Math.min(caretPos, maxTemplateIDLength); try { Document doc = textArea.getDocument(); doc.getText(caretPos - charsToGet, charsToGet, s); int index = Collections.binarySearch(templates, s, comparator); return index >= 0 ? (CodeTemplate) templates.get(index) : null; } catch (BadLocationException ble) { ble.printStackTrace(); throw new InternalError("Error in CodeTemplateManager"); } }
public static void setFont(RSyntaxTextArea textArea, Font font) { if (font != null) { SyntaxScheme ss = textArea.getSyntaxScheme(); ss = (SyntaxScheme) ss.clone(); for (int i = 0; i < ss.getStyleCount(); i++) { if (ss.getStyle(i) != null) { ss.getStyle(i).font = font; } } textArea.setSyntaxScheme(ss); textArea.setFont(font); } }
/** * Determines the position in the model that is closest to the given view location in the row * below. The component given must have a size to compute the result. If the component doesn't * have a size a value of -1 will be returned. * * @param c the editor * @param offs the offset in the document >= 0 * @param x the X coordinate >= 0 * @return the position >= 0 if the request can be computed, otherwise a value of -1 will be * returned. * @exception BadLocationException if the offset is out of range */ public static final int getPositionBelow(RSyntaxTextArea c, int offs, float x, TabExpander e) throws BadLocationException { TokenOrientedView tov = (TokenOrientedView) e; Token token = tov.getTokenListForPhysicalLineBelow(offs); if (token == null) return -1; // A line containing only Token.NULL is an empty line. else if (token.type == Token.NULL) { int line = c.getLineOfOffset(offs); // Sure to be > c.getLineCount()-1 ?? return c.getLineStartOffset(line + 1); } else { return token.getListOffset(c, e, 0, x); } }
/** * Provides a way to determine the next visually represented model location at which one might * place a caret. Some views may not be visible, they might not be in the same order found in the * model, or they just might not allow access to some of the locations in the model. * * <p>NOTE: You should only call this method if the passed-in <code>javax.swing.text.View</code> * is an instance of {@link TokenOrientedView} and <code>javax.swing.text.TabExpander</code>; * otherwise, a <code>ClassCastException</code> could be thrown. * * @param pos the position to convert >= 0 * @param a the allocated region in which to render * @param direction the direction from the current position that can be thought of as the arrow * keys typically found on a keyboard. This will be one of the following values: * <ul> * <li>SwingConstants.WEST * <li>SwingConstants.EAST * <li>SwingConstants.NORTH * <li>SwingConstants.SOUTH * </ul> * * @return the location within the model that best represents the next location visual position * @exception BadLocationException * @exception IllegalArgumentException if <code>direction</code> doesn't have one of the legal * values above */ public static int getNextVisualPositionFrom( int pos, Position.Bias b, Shape a, int direction, Position.Bias[] biasRet, View view) throws BadLocationException { biasRet[0] = Position.Bias.Forward; // Do we want the "next position" above, below, to the left or right? switch (direction) { case NORTH: case SOUTH: if (pos == -1) { pos = (direction == NORTH) ? Math.max(0, view.getEndOffset() - 1) : view.getStartOffset(); break; } RSyntaxTextArea target = (RSyntaxTextArea) view.getContainer(); Caret c = (target != null) ? target.getCaret() : null; // YECK! Ideally, the x location from the magic caret // position would be passed in. Point mcp; if (c != null) mcp = c.getMagicCaretPosition(); else mcp = null; int x; if (mcp == null) { Rectangle loc = target.modelToView(pos); x = (loc == null) ? 0 : loc.x; } else { x = mcp.x; } if (direction == NORTH) pos = getPositionAbove(target, pos, x, (TabExpander) view); else pos = getPositionBelow(target, pos, x, (TabExpander) view); break; case WEST: if (pos == -1) pos = Math.max(0, view.getEndOffset() - 1); else pos = Math.max(0, pos - 1); break; case EAST: if (pos == -1) pos = view.getStartOffset(); else pos = Math.min(pos + 1, view.getDocument().getLength()); break; default: throw new IllegalArgumentException("Bad direction: " + direction); } return pos; }
public float getWidthUpTo(int numChars, RSyntaxTextArea textArea, TabExpander e, float x0) { float width = x0; FontMetrics fm = textArea.getFontMetricsForTokenType(getType()); if (fm != null) { int w; int currentStart = textOffset; int endBefore = textOffset + numChars; for (int i = currentStart; i < endBefore; i++) { if (text[i] == '\t') { // Since TokenMaker implementations usually group all // adjacent whitespace into a single token, there // aren't usually any characters to compute a width // for here, so we check before calling. w = i - currentStart; if (w > 0) width += fm.charsWidth(text, currentStart, w); currentStart = i + 1; width = e.nextTabStop(width, 0); } } // Most (non-whitespace) tokens will have characters at this // point to get the widths for, so we don't check for w>0 (mini- // optimization). w = endBefore - currentStart; width += fm.charsWidth(text, currentStart, w); } return width - x0; }
/** * Returns the start of the word at the given offset. * * @param textArea The text area. * @param offs The offset into the text area's content. * @return The start offset of the word. * @throws BadLocationException If <code>offs</code> is invalid. * @see #getWordEnd(RSyntaxTextArea, int) */ public static int getWordStart(RSyntaxTextArea textArea, int offs) throws BadLocationException { Document doc = textArea.getDocument(); Element line = getLineElem(doc, offs); if (line == null) { throw new BadLocationException("No word at " + offs, offs); } int lineStart = line.getStartOffset(); if (offs == lineStart) { // Start of the line. return offs; } int endOffs = Math.min(offs + 1, doc.getLength()); String s = doc.getText(lineStart, endOffs - lineStart); if (s != null && s.length() > 0) { int i = s.length() - 1; char ch = s.charAt(i); if (Character.isWhitespace(ch)) { while (i > 0 && Character.isWhitespace(s.charAt(i - 1))) { i--; } offs = lineStart + i; } else if (Character.isLetterOrDigit(ch)) { while (i > 0 && Character.isLetterOrDigit(s.charAt(i - 1))) { i--; } offs = lineStart + i; } } return offs; }
/** * Workaround for JTextComponents allowing the caret to be rendered entirely off-screen if the * entire "previous" character fit entirely. * * @return The amount of space to add to the x-axis preferred span. */ private int getRhsCorrection() { int rhsCorrection = 10; if (host != null) { rhsCorrection = host.getRightHandSideCorrection(); } return rhsCorrection; }
/** Overridden to allow for folded regions. */ @Override protected View getViewAtPoint(int x, int y, Rectangle alloc) { int lineCount = getViewCount(); int curY = alloc.y + getOffset(Y_AXIS, 0); // Always at least 1 line host = (RSyntaxTextArea) getContainer(); FoldManager fm = host.getFoldManager(); for (int line = 1; line < lineCount; line++) { int span = getSpan(Y_AXIS, line - 1); if (y < curY + span) { childAllocation2(line - 1, curY, alloc); return getView(line - 1); } curY += span; Fold fold = fm.getFoldForLine(line - 1); if (fold != null && fold.isCollapsed()) { line += fold.getCollapsedLineCount(); } } // Not found - return last line's view. childAllocation2(lineCount - 1, curY, alloc); return getView(lineCount - 1); }
/** * Returns the bounding box (in the current view) of a specified position in the model. This * method is designed for line-wrapped views to use, as it allows you to specify a "starting * position" in the line, from which the x-value is assumed to be zero. The idea is that you * specify the first character in a physical line as <code>p0</code>, as this is the character * where the x-pixel value is 0. * * @param textArea The text area containing the text. * @param s A segment in which to load the line. This is passed in so we don't have to reallocate * a new <code>Segment</code> for each call. * @param p0 The starting position in the physical line in the document. * @param p1 The position for which to get the bounding box in the view. * @param e How to expand tabs. * @param rect The rectangle whose x- and width-values are changed to represent the bounding box * of <code>p1</code>. This is reused to keep from needlessly reallocating Rectangles. * @param x0 The x-coordinate (pixel) marking the left-hand border of the text. This is useful if * the text area has a border, for example. * @return The bounding box in the view of the character <code>p1</code>. * @throws BadLocationException If <code>p0</code> or <code>p1</code> is not a valid location in * the specified text area's document. * @throws IllegalArgumentException If <code>p0</code> and <code>p1</code> are not on the same * line. */ public static Rectangle getLineWidthUpTo( RSyntaxTextArea textArea, Segment s, int p0, int p1, TabExpander e, Rectangle rect, int x0) throws BadLocationException { RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument(); // Ensure p0 and p1 are valid document positions. if (p0 < 0) throw new BadLocationException("Invalid document position", p0); else if (p1 > doc.getLength()) throw new BadLocationException("Invalid document position", p1); // Ensure p0 and p1 are in the same line, and get the start/end // offsets for that line. Element map = doc.getDefaultRootElement(); int lineNum = map.getElementIndex(p0); // We do ">1" because p1 might be the first position on the next line // or the last position on the previous one. // if (lineNum!=map.getElementIndex(p1)) if (Math.abs(lineNum - map.getElementIndex(p1)) > 1) throw new IllegalArgumentException( "p0 and p1 are not on the " + "same line (" + p0 + ", " + p1 + ")."); // Get the token list. Token t = doc.getTokenListForLine(lineNum); // Modify the token list 't' to begin at p0 (but still have correct // token types, etc.), and get the x-location (in pixels) of the // beginning of this new token list. makeTokenListStartAt(t, p0, e, textArea, 0); rect = t.listOffsetToView(textArea, e, p1, x0, rect); return rect; }
@Override public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, final int offs) { Token token = textArea.modelToToken(offs); if (token == null) { return null; } String reference = pdeKeywords.getReference(token.getLexeme()); if (reference != null || (token.getType() == TokenTypes.DATA_TYPE || token.getType() == TokenTypes.VARIABLE || token.getType() == TokenTypes.FUNCTION)) { return new LinkGeneratorResult() { @Override public int getSourceOffset() { return offs; } @Override public HyperlinkEvent execute() { LOG.fine("Open Reference: " + reference); Base.showReference("Reference/" + reference); return null; } }; } return null; }
/** * Paints the word-wrapped text. * * @param g The graphics context in which to paint. * @param a The shape (usually a rectangle) in which to paint. */ public void paint(Graphics g, Shape a) { Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a : a.getBounds(); tabBase = alloc.x; Graphics2D g2d = (Graphics2D) g; host = (RSyntaxTextArea) getContainer(); int ascent = host.getMaxAscent(); int fontHeight = host.getLineHeight(); FoldManager fm = host.getFoldManager(); TokenPainter painter = host.getTokenPainter(); Element root = getElement(); // Whether token styles should always be painted, even in selections int selStart = host.getSelectionStart(); int selEnd = host.getSelectionEnd(); boolean useSelectedTextColor = host.getUseSelectedTextColor(); int n = getViewCount(); // Number of lines. int x = alloc.x + getLeftInset(); tempRect.y = alloc.y + getTopInset(); Rectangle clip = g.getClipBounds(); for (int i = 0; i < n; i++) { tempRect.x = x + getOffset(X_AXIS, i); // tempRect.y = y + getOffset(Y_AXIS, i); tempRect.width = getSpan(X_AXIS, i); tempRect.height = getSpan(Y_AXIS, i); // System.err.println("For line " + i + ": tempRect==" + tempRect); if (tempRect.intersects(clip)) { Element lineElement = root.getElement(i); int startOffset = lineElement.getStartOffset(); int endOffset = lineElement.getEndOffset() - 1; // Why always "-1"? View view = getView(i); if (!useSelectedTextColor || selStart == selEnd || (startOffset >= selEnd || endOffset < selStart)) { drawView(painter, g2d, alloc, view, fontHeight, tempRect.y + ascent); } else { // System.out.println("Drawing line with selection: " + i); drawViewWithSelection( painter, g2d, alloc, view, fontHeight, tempRect.y + ascent, selStart, selEnd); } } tempRect.y += tempRect.height; Fold possibleFold = fm.getFoldForLine(i); if (possibleFold != null && possibleFold.isCollapsed()) { i += possibleFold.getCollapsedLineCount(); // Visible indicator of collapsed lines Color c = RSyntaxUtilities.getFoldedLineBottomColor(host); if (c != null) { g.setColor(c); g.drawLine(x, tempRect.y - 1, alloc.width, tempRect.y - 1); } } } }
/** {@inheritDoc} */ public int yForLineContaining(Rectangle alloc, int offs) throws BadLocationException { if (isAllocationValid()) { // TODO: make cached Y_AXIS offsets valid even with folding enabled // to speed this back up! Rectangle r = (Rectangle) modelToView(offs, alloc, Bias.Forward); if (r != null) { if (host.isCodeFoldingEnabled()) { int line = host.getLineOfOffset(offs); FoldManager fm = host.getFoldManager(); if (fm.isLineHidden(line)) { return -1; } } return r.y; } } return -1; }
/** {@inheritDoc} */ public int yForLine(Rectangle alloc, int line) throws BadLocationException { // Rectangle lineArea = lineToRect(alloc, lineIndex); updateMetrics(); if (metrics != null) { // NOTE: lineHeight is not initially set here, leading to the // current line not being highlighted when a document is first // opened. So, we set it here just in case. lineHeight = host != null ? host.getLineHeight() : lineHeight; FoldManager fm = host.getFoldManager(); if (!fm.isLineHidden(line)) { line -= fm.getHiddenLineCountAbove(line); return alloc.y + line * lineHeight; } } return -1; }
/** * Determine the rectangle that represents the given line. * * @param a The region allocated for the view to render into * @param line The line number to find the region of. This must be a valid line number in the * model. */ protected Rectangle lineToRect(Shape a, int line) { Rectangle r = null; updateMetrics(); if (metrics != null) { Rectangle alloc = a.getBounds(); // NOTE: lineHeight is not initially set here, leading to the // current line not being highlighted when a document is first // opened. So, we set it here just in case. lineHeight = host != null ? host.getLineHeight() : lineHeight; if (host != null && host.isCodeFoldingEnabled()) { FoldManager fm = host.getFoldManager(); int hiddenCount = fm.getHiddenLineCountAbove(line); line -= hiddenCount; } r = new Rectangle(alloc.x, alloc.y + line * lineHeight, alloc.width, lineHeight); } return r; }
/** Checks to see if the font metrics and longest line are up-to-date. */ private void updateMetrics() { host = (RSyntaxTextArea) getContainer(); Font f = host.getFont(); if (font != f) { // The font changed, we need to recalculate the longest line! // This also updates cached font and tab size. calculateLongestLine(); } }
private void changeStyleProgrammatically() { // Set the font for all token types. setFont(textArea, new Font("Courier New", Font.PLAIN, 12)); // Change a few things here and there. SyntaxScheme scheme = textArea.getSyntaxScheme(); scheme.getStyle(Token.RESERVED_WORD).background = Color.white; scheme.getStyle(Token.RESERVED_WORD).foreground = Color.MAGENTA.darker().darker(); scheme.getStyle(Token.DATA_TYPE).foreground = Color.blue; scheme.getStyle(Token.LITERAL_STRING_DOUBLE_QUOTE).underline = true; scheme.getStyle(Token.LITERAL_NUMBER_HEXADECIMAL).underline = true; scheme.getStyle(Token.LITERAL_NUMBER_HEXADECIMAL).background = Color.pink; scheme.getStyle(Token.COMMENT_EOL).font = new Font("Georgia", Font.ITALIC, 10); textArea.revalidate(); }
private void insertBreakInMLC(ActionEvent e, RSyntaxTextArea textArea, int line) { Matcher m = null; int start = -1; int end = -1; try { start = textArea.getLineStartOffset(line); end = textArea.getLineEndOffset(line); String text = textArea.getText(start, end - start); m = p.matcher(text); } catch (BadLocationException ble) { // Never happens UIManager.getLookAndFeel().provideErrorFeedback(textArea); ble.printStackTrace(); return; } if (m.lookingAt()) { String leadingWS = m.group(1); String mlcMarker = m.group(2); // If the caret is "inside" any leading whitespace or MLC // marker, move it to the end of the line. int dot = textArea.getCaretPosition(); if (dot >= start && dot < start + leadingWS.length() + mlcMarker.length()) { // If we're in the whitespace before the very start of the // MLC though, just insert a normal newline if (mlcMarker.charAt(0) == '/') { handleInsertBreak(textArea, true); return; } textArea.setCaretPosition(end - 1); } boolean firstMlcLine = mlcMarker.charAt(0) == '/'; boolean nested = appearsNested(textArea, line, start + leadingWS.length() + 2); String header = leadingWS + (firstMlcLine ? " * " : "*") + m.group(3); textArea.replaceSelection("\n" + header); if (nested) { dot = textArea.getCaretPosition(); // Has changed textArea.insert("\n" + leadingWS + " */", dot); textArea.setCaretPosition(dot); } } else { handleInsertBreak(textArea, true); } }
/** * Sets the allocation rectangle for a given line's view, but sets the y value to the passed-in * value. This should be used instead of {@link #childAllocation(int, Rectangle)} since it allows * you to account for hidden lines in collapsed fold regions. * * @param line * @param y * @param alloc */ private void childAllocation2(int line, int y, Rectangle alloc) { alloc.x += getOffset(X_AXIS, line); alloc.y += y; alloc.width = getSpan(X_AXIS, line); alloc.height = getSpan(Y_AXIS, line); // FIXME: This is required due to a bug that I can't track down. The // top margin is being added twice somewhere in wrapped views, so we // have to adjust for it here. alloc.y -= host.getMargin().top; }
private void handleDocumentEvent(DocumentEvent e, Shape a, ViewFactory f) { int n = calculateLineCount(); if (this.nlines != n) { this.nlines = n; WrappedSyntaxView.this.preferenceChanged(this, false, true); // have to repaint any views after the receiver. RSyntaxTextArea textArea = (RSyntaxTextArea) getContainer(); textArea.repaint(); // Must also revalidate container so gutter components, such // as line numbers, get updated for this line's new height Gutter gutter = RSyntaxUtilities.getGutter(textArea); if (gutter != null) { gutter.revalidate(); gutter.repaint(); } } else if (a != null) { Component c = getContainer(); Rectangle alloc = (Rectangle) a; c.repaint(alloc.x, alloc.y, alloc.width, alloc.height); } }
/** * Appends an HTML version of the lexeme of this token (i.e. no style HTML, but replacing chars * such as <code>\t</code>, <code><</code> and <code>></code> with their escapes). * * @param textArea The text area. * @param sb The buffer to append to. * @param tabsToSpaces Whether to convert tabs into spaces. * @return The same buffer. */ private final StringBuilder appendHtmlLexeme( RSyntaxTextArea textArea, StringBuilder sb, boolean tabsToSpaces) { boolean lastWasSpace = false; int i = textOffset; int lastI = i; String tabStr = null; while (i < textOffset + textCount) { char ch = text[i]; switch (ch) { case ' ': sb.append(text, lastI, i - lastI); lastI = i + 1; sb.append(lastWasSpace ? " " : " "); lastWasSpace = true; break; case '\t': sb.append(text, lastI, i - lastI); lastI = i + 1; if (tabsToSpaces && tabStr == null) { tabStr = ""; for (int j = 0; j < textArea.getTabSize(); j++) { tabStr += " "; } } sb.append(tabsToSpaces ? tabStr : "	"); lastWasSpace = false; break; case '<': sb.append(text, lastI, i - lastI); lastI = i + 1; sb.append("<"); lastWasSpace = false; break; case '>': sb.append(text, lastI, i - lastI); lastI = i + 1; sb.append(">"); lastWasSpace = false; break; default: lastWasSpace = false; break; } i++; } if (lastI < textOffset + textCount) { sb.append(text, lastI, textOffset + textCount - lastI); } return sb; }
/** * Draws the passed-in text using syntax highlighting for the current language. It is assumed that * the entire line is either not in a selected region, or painting with a selection-foreground * color is turned off. * * @param painter The painter to render the tokens. * @param token The list of tokens to draw. * @param g The graphics context in which to draw. * @param x The x-coordinate at which to draw. * @param y The y-coordinate at which to draw. * @return The x-coordinate representing the end of the painted text. */ private float drawLine( TokenPainter painter, Token token, Graphics2D g, float x, float y, int line) { float nextX = x; // The x-value at the end of our text. boolean paintBG = host.getPaintTokenBackgrounds(line, y); while (token != null && token.isPaintable() && nextX < clipEnd) { nextX = painter.paint(token, g, nextX, y, host, this, clipStart, paintBG); token = token.getNextToken(); } // NOTE: We should re-use code from Token (paintBackground()) here, // but don't because I'm just too lazy. if (host.getEOLMarkersVisible()) { g.setColor(host.getForegroundForTokenType(Token.WHITESPACE)); g.setFont(host.getFontForTokenType(Token.WHITESPACE)); g.drawString("\u00B6", nextX, y); } // Return the x-coordinate at the end of the painted text. return nextX; }
/** * Returns a token list for the <i>physical</i> line below the physical line containing the * specified offset into the document. Note that for this plain (non-wrapped) view, this is simply * the token list for the logical line below the line containing <code>offset</code>, since lines * are not wrapped. * * @param offset The offset in question. * @return A token list for the physical (and in this view, logical) line after this one. If * <code>offset</code> is in the last physical line in the document, <code>null</code> is * returned. */ public Token getTokenListForPhysicalLineBelow(int offset) { RSyntaxDocument document = (RSyntaxDocument) getDocument(); Element map = document.getDefaultRootElement(); int lineCount = map.getElementCount(); int line = map.getElementIndex(offset); if (!host.isCodeFoldingEnabled()) { if (line < lineCount - 1) { return document.getTokenListForLine(line + 1); } } else { FoldManager fm = host.getFoldManager(); line = fm.getVisibleLineBelow(line); if (line >= 0 && line < lineCount) { return document.getTokenListForLine(line); } } // int line = map.getElementIndex(offset); // int lineCount = map.getElementCount(); // if (line<lineCount-1) // return document.getTokenListForLine(line+1); return null; }
/** * Paints the word-wrapped text. * * @param g The graphics context in which to paint. * @param a The shape (usually a rectangle) in which to paint. */ public void paint(Graphics g, Shape a) { Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a : a.getBounds(); tabBase = alloc.x; Graphics2D g2d = (Graphics2D) g; host = (RSyntaxTextArea) getContainer(); int ascent = host.getMaxAscent(); int fontHeight = host.getLineHeight(); FoldManager fm = host.getFoldManager(); int n = getViewCount(); // Number of lines. int x = alloc.x + getLeftInset(); tempRect.y = alloc.y + getTopInset(); Rectangle clip = g.getClipBounds(); for (int i = 0; i < n; i++) { tempRect.x = x + getOffset(X_AXIS, i); // tempRect.y = y + getOffset(Y_AXIS, i); tempRect.width = getSpan(X_AXIS, i); tempRect.height = getSpan(Y_AXIS, i); // System.err.println("For line " + i + ": tempRect==" + tempRect); if (tempRect.intersects(clip)) { View view = getView(i); drawView(g2d, alloc, view, fontHeight, tempRect.y + ascent); } tempRect.y += tempRect.height; Fold possibleFold = fm.getFoldForLine(i); if (possibleFold != null && possibleFold.isCollapsed()) { i += possibleFold.getCollapsedLineCount(); // Visible indicator of collapsed lines Color c = RSyntaxUtilities.getFoldedLineBottomColor(host); if (c != null) { g.setColor(c); g.drawLine(x, tempRect.y - 1, alloc.width, tempRect.y - 1); } } } }