📄 jedittextarea.java
字号:
/* * JEditTextArea.java - jEdit's text component * Copyright (C) 1999, 2000, 2001 Slava Pestov * Portions copyright (C) 2000 Ollie Rutherfurd * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */package org.gjt.sp.jedit.textarea;import javax.swing.border.*;import javax.swing.event.*;import javax.swing.plaf.metal.MetalLookAndFeel;import javax.swing.text.BadLocationException;import javax.swing.text.Element;import javax.swing.text.Segment;import javax.swing.text.Utilities;import javax.swing.undo.*;import javax.swing.*;import java.awt.event.*;import java.awt.*;import java.util.Enumeration;import java.util.Hashtable;import java.util.Vector;import org.gjt.sp.jedit.gui.*;import org.gjt.sp.jedit.syntax.*;import org.gjt.sp.jedit.*;import org.gjt.sp.util.Log;/** * jEdit's text component. * * @author Slava Pestov * @version $Id: JEditTextArea.java,v 1.2 2001/09/04 06:45:35 spestov Exp $ */public class JEditTextArea extends JComponent{ /** * Creates a new JEditTextArea. */ public JEditTextArea(View view) { enableEvents(AWTEvent.FOCUS_EVENT_MASK | AWTEvent.KEY_EVENT_MASK); this.view = view; // Initialize some misc. stuff selection = new Vector(); renderer = TextRenderer.createTextRenderer(); painter = new TextAreaPainter(this); gutter = new Gutter(view,this); documentHandler = new DocumentHandler(); foldHandler = new FoldHandler(); listenerList = new EventListenerList(); caretEvent = new MutableCaretEvent(); bracketLine = bracketPosition = -1; blink = true; lineSegment = new Segment(); // Initialize the GUI setLayout(new ScrollLayout()); add(LEFT,gutter); add(CENTER,painter); add(RIGHT,vertical = new JScrollBar(JScrollBar.VERTICAL)); add(BOTTOM,horizontal = new JScrollBar(JScrollBar.HORIZONTAL)); horizontal.setValues(0,0,0,0); // this ensures that the text area's look is slightly // more consistent with the rest of the metal l&f. // while it depends on not-so-well-documented portions // of Swing, it only affects appearance, so future // breakage shouldn't matter if(UIManager.getLookAndFeel() instanceof MetalLookAndFeel) { setBorder(new TextAreaBorder()); vertical.putClientProperty("JScrollBar.isFreeStanding", Boolean.FALSE); horizontal.putClientProperty("JScrollBar.isFreeStanding", Boolean.FALSE); //horizontal.setBorder(null); } // Add some event listeners vertical.addAdjustmentListener(new AdjustHandler()); horizontal.addAdjustmentListener(new AdjustHandler()); painter.addComponentListener(new ComponentHandler()); mouseHandler = new MouseHandler(); painter.addMouseListener(mouseHandler); painter.addMouseMotionListener(mouseHandler); addFocusListener(new FocusHandler()); // This doesn't seem very correct, but it fixes a problem // when setting the initial caret position for a buffer // (eg, from the recent file list) focusedComponent = this; } /** * Returns the object responsible for painting this text area. */ public final TextAreaPainter getPainter() { return painter; } /** * Returns the gutter to the left of the text area or null if the gutter * is disabled */ public final Gutter getGutter() { return gutter; } /** * Returns true if the caret is blinking, false otherwise. */ public final boolean isCaretBlinkEnabled() { return caretBlinks; } /** * Toggles caret blinking. * @param caretBlinks True if the caret should blink, false otherwise */ public void setCaretBlinkEnabled(boolean caretBlinks) { this.caretBlinks = caretBlinks; if(!caretBlinks) blink = false; if(buffer != null) invalidateLine(caretLine); } /** * Blinks the caret. */ public final void blinkCaret() { if(caretBlinks) { blink = !blink; invalidateLine(caretLine); } else blink = true; } /** * Returns the number of lines from the top and button of the * text area that are always visible. */ public final int getElectricScroll() { return electricScroll; } /** * Sets the number of lines from the top and bottom of the text * area that are always visible * @param electricScroll The number of lines always visible from * the top or bottom */ public final void setElectricScroll(int electricScroll) { this.electricScroll = electricScroll; } /** * Returns if clicking the middle mouse button pastes the most * recent selection (% register). */ public final boolean isMiddleMousePasteEnabled() { return middleMousePaste; } /** * Sets if clicking the middle mouse button pastes the most * recent selection (% register). * @param middleMousePaste A boolean flag */ public final void setMiddleMousePasteEnabled(boolean middleMousePaste) { this.middleMousePaste = middleMousePaste; } /** * Updates the state of the scroll bars. This should be called * if the number of lines in the buffer changes, or when the * size of the text are changes. */ public void updateScrollBars() { if(vertical != null && visibleLines != 0) { // don't display stuff past the end of the buffer if // we can help it int lineCount = getVirtualLineCount(); if(lineCount < firstLine + visibleLines) { // this will call updateScrollBars(), so // just return... int newFirstLine = Math.max(0,lineCount - visibleLines); if(newFirstLine != firstLine) { setFirstLine(newFirstLine); return; } } vertical.setValues(firstLine,visibleLines,0,lineCount); vertical.setUnitIncrement(2); vertical.setBlockIncrement(visibleLines); } int width = painter.getWidth(); if(horizontal != null && width != 0) { maxHorizontalScrollWidth = 0; painter.repaint(); horizontal.setUnitIncrement(painter.getFontMetrics() .charWidth('w')); horizontal.setBlockIncrement(width / 2); } } /** * Returns the line displayed at the text area's origin. This is * a virtual, not a physical, line number. */ public final int getFirstLine() { return firstLine; } /** * Sets the line displayed at the text area's origin. This is * a virtual, not a physical, line number. */ public void setFirstLine(int firstLine) { if(firstLine == this.firstLine) return; _setFirstLine(firstLine); view.synchroScrollVertical(this,firstLine); } public void _setFirstLine(int firstLine) { this.firstLine = Math.max(0,firstLine); physFirstLine = buffer.virtualToPhysical(this.firstLine); maxHorizontalScrollWidth = 0; // hack so that if we scroll and the matching bracket // comes into view, it is highlighted // 3.2pre9 update: I am commenting this out once again because // I have changed the location of the documentChanged() call // in the DocumentHandler, so this is called before the caret // position is updated, which can be potentially tricky. //if(bracketPosition == -1) // updateBracketHighlight(); if(this.firstLine != vertical.getValue()) updateScrollBars(); painter.repaint(); gutter.repaint(); fireScrollEvent(true); } /** * Returns the number of lines visible in this text area. */ public final int getVisibleLines() { return visibleLines; } /** * Returns the horizontal offset of drawn lines. */ public final int getHorizontalOffset() { return horizontalOffset; } /** * Sets the horizontal offset of drawn lines. This can be used to * implement horizontal scrolling. * @param horizontalOffset offset The new horizontal offset */ public void setHorizontalOffset(int horizontalOffset) { if(horizontalOffset == this.horizontalOffset) return; _setHorizontalOffset(horizontalOffset); view.synchroScrollHorizontal(this,horizontalOffset); } public void _setHorizontalOffset(int horizontalOffset) { this.horizontalOffset = horizontalOffset; if(horizontalOffset != horizontal.getValue()) updateScrollBars(); painter.repaint(); fireScrollEvent(false); } /** * @deprecated Use setFirstLine() and setHorizontalOffset() instead */ public boolean setOrigin(int firstLine, int horizontalOffset) { setFirstLine(firstLine); setHorizontalOffset(horizontalOffset); return true; } /** * Centers the caret on the screen. * @since jEdit 2.7pre2 */ public void centerCaret() { Element map = buffer.getDefaultRootElement(); int gotoLine = buffer.virtualToPhysical(firstLine + visibleLines / 2); if(gotoLine < 0 || gotoLine >= map.getElementCount()) { getToolkit().beep(); return; } Element element = map.getElement(gotoLine); setCaretPosition(element.getStartOffset()); } /** * Scrolls up by one line. * @since jEdit 2.7pre2 */ public void scrollUpLine() { if(firstLine > 0) setFirstLine(firstLine-1); else getToolkit().beep(); } /** * Scrolls up by one page. * @since jEdit 2.7pre2 */ public void scrollUpPage() { if(firstLine > 0) { int newFirstLine = firstLine - visibleLines; setFirstLine(newFirstLine); } else { getToolkit().beep(); } } /** * Scrolls down by one line. * @since jEdit 2.7pre2 */ public void scrollDownLine() { int numLines = getVirtualLineCount(); if(firstLine + visibleLines < numLines) setFirstLine(firstLine + 1); else getToolkit().beep(); } /** * Scrolls down by one page. * @since jEdit 2.7pre2 */ public void scrollDownPage() { int numLines = getVirtualLineCount(); if(firstLine + visibleLines < numLines) { int newFirstLine = firstLine + visibleLines; setFirstLine(newFirstLine + visibleLines < numLines ? newFirstLine : numLines - visibleLines); } else { getToolkit().beep(); } } /** * Ensures that the caret is visible by scrolling the text area if * necessary. * @param doElectricScroll If true, electric scrolling will be performed */ public void scrollToCaret(boolean doElectricScroll) { if(!buffer.isLineVisible(caretLine)) buffer.expandFoldAt(caretLine,true,this); int offset = caret - getLineStartOffset(caretLine); int virtualCaretLine = buffer.physicalToVirtual(caretLine); // visibleLines == 0 before the component is realized // we can't do any proper scrolling then, so we have // this hack... if(visibleLines == 0) { setFirstLine(caretLine - electricScroll); return; } int lineCount = getVirtualLineCount(); int _lastLine = firstLine + visibleLines; int electricScroll; if(doElectricScroll && visibleLines > this.electricScroll * 2) electricScroll = this.electricScroll; else electricScroll = 0; boolean changed = false; int _firstLine = (firstLine == 0 ? 0 : firstLine + electricScroll); if(_lastLine >= lineCount - 1) _lastLine = lineCount - 1; else _lastLine -= electricScroll; if(virtualCaretLine > _firstLine && virtualCaretLine < _lastLine) { // vertical scroll position is correct already } else if(_firstLine - virtualCaretLine > visibleLines || virtualCaretLine - _lastLine > visibleLines) { int startLine, endLine; Selection s = getSelectionAtOffset(caret); if(s == null) { startLine = endLine = virtualCaretLine; } else { startLine = buffer.physicalToVirtual(s.startLine); endLine = buffer.physicalToVirtual(s.endLine); } if(endLine - startLine <= visibleLines) firstLine = (startLine + endLine - visibleLines) / 2; else firstLine = buffer.physicalToVirtual(caretLine) - visibleLines / 2; firstLine = Math.min(firstLine,buffer.getVirtualLineCount() - visibleLines); firstLine = Math.max(firstLine,0); changed = true; } else if(virtualCaretLine < _firstLine) { firstLine = Math.max(0,virtualCaretLine - electricScroll); changed = true; } else if(virtualCaretLine >= _lastLine) { firstLine = (virtualCaretLine - visibleLines) + electricScroll + 1; if(firstLine >= getVirtualLineCount() - visibleLines) firstLine = getVirtualLineCount() - visibleLines; changed = true; } int x = offsetToX(caretLine,offset); int width = painter.getFontMetrics().charWidth('w'); if(x < 0) { horizontalOffset = Math.min(0,horizontalOffset - x + width + 5); changed = true; } else if(x >= painter.getWidth() - width - 5) { horizontalOffset = horizontalOffset + (painter.getWidth() - x) - width - 5; changed = true; } if(changed) { if(firstLine < 0) firstLine = 0; physFirstLine = buffer.virtualToPhysical(firstLine); updateScrollBars(); painter.repaint(); gutter.repaint(); view.synchroScrollVertical(this,firstLine); view.synchroScrollHorizontal(this,horizontalOffset); // fire events for both a horizontal and vertical scroll fireScrollEvent(true); fireScrollEvent(false); } } /** * Converts a line index to a y co-ordinate. This must be a virtual, * not a physical, line number. * @param line The line */ public int lineToY(int line) { FontMetrics fm = painter.getFontMetrics(); return (line - firstLine) * fm.getHeight() - (fm.getLeading() + fm.getDescent()); } /** * Converts a y co-ordinate to a virtual line index. * @param y The y co-ordinate */ public int yToLine(int y) { FontMetrics fm = painter.getFontMetrics(); int height = fm.getHeight(); return Math.max(0,Math.min(getVirtualLineCount() - 1, y / height + firstLine)); } /** * Returns the text renderer instance. This method is going away in * the next major release, so do not use it. * @since jEdit 3.2pre6 */ public TextRenderer getTextRenderer() { return renderer; } /** * Converts an offset in a line into an x co-ordinate. * @param line The line * @param offset The offset, from the start of the line */ public int offsetToX(int line, int offset) { Token tokens = buffer.markTokens(line).getFirstToken(); getLineText(line,lineSegment); char[] text = lineSegment.array; int off = lineSegment.offset; float x = (float)horizontalOffset; Toolkit toolkit = painter.getToolkit(); Font defaultFont = painter.getFont(); SyntaxStyle[] styles = painter.getStyles(); for(;;) { byte id = tokens.id; if(id == Token.END) return (int)x; Font font; if(id == Token.NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -