📄 defaultcontent.java
字号:
/******************************************************************************* * Copyright (c) 2000, 2004 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/package org.eclipse.swt.custom;import org.eclipse.swt.*;import org.eclipse.swt.internal.Compatibility;import org.eclipse.swt.widgets.*;import java.util.Vector;class DefaultContent implements StyledTextContent { private final static String LineDelimiter = System.getProperty("line.separator"); Vector textListeners = new Vector(); // stores text listeners for event sending char[] textStore = new char[0]; // stores the actual text int gapStart = -1; // the character position start of the gap int gapEnd = -1; // the character position after the end of the gap int gapLine = -1; // the line on which the gap exists, the gap will always be associated // with one line int highWatermark = 300; int lowWatermark = 50; int[][] lines = new int[50][2]; // array of character positions and lengths representing // the lines of text int lineCount = 0; // the number of lines of text int expandExp = 1; // the expansion exponent, used to increase the lines array exponentially int replaceExpandExp = 1; // the expansion exponent, used to increase the lines array exponentially/** * Creates a new DefaultContent and initializes it. A <code>StyledTextContent</> will always have * at least one empty line. */DefaultContent() { super(); setText("");}/** * Adds a line to the end of the line indexes array. Increases the size of the array if necessary. * <code>lineCount</code> is updated to reflect the new entry. * <p> * * @param start the start of the line * @param length the length of the line */void addLineIndex(int start, int length) { int size = lines.length; if (lineCount == size) { // expand the lines by powers of 2 int[][] newLines = new int[size+Compatibility.pow2(expandExp)][2]; System.arraycopy(lines, 0, newLines, 0, size); lines = newLines; expandExp++; } int[] range = new int[] {start, length}; lines[lineCount] = range; lineCount++;}/** * Adds a line index to the end of <code>linesArray</code>. Increases the * size of the array if necessary and returns a new array. * <p> * * @param start the start of the line * @param length the length of the line * @param linesArray the array to which to add the line index * @param count the position at which to add the line * @return a new array of line indexes */int[][] addLineIndex(int start, int length, int[][] linesArray, int count) { int size = linesArray.length; int[][] newLines = linesArray; if (count == size) { newLines = new int[size+Compatibility.pow2(replaceExpandExp)][2]; replaceExpandExp++; System.arraycopy(linesArray, 0, newLines, 0, size); } int[] range = new int[] {start, length}; newLines[count] = range; return newLines;}/** * Adds a <code>TextChangeListener</code> listening for * <code>TextChangingEvent</code> and <code>TextChangedEvent</code>. A * <code>TextChangingEvent</code> is sent before changes to the text occur. * A <code>TextChangedEvent</code> is sent after changes to the text * occured. * <p> * * @param listener the listener * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT when listener is null</li> * </ul> */public void addTextChangeListener(TextChangeListener listener) { if (listener == null) error(SWT.ERROR_NULL_ARGUMENT); StyledTextListener typedListener = new StyledTextListener(listener); textListeners.addElement(typedListener); } /** * Adjusts the gap to accomodate a text change that is occurring. * <p> * * @param position the position at which a change is occurring * @param sizeHint the size of the change * @param line the line where the gap will go */void adjustGap(int position, int sizeHint, int line) { if (position == gapStart) { // text is being inserted at the gap position int size = (gapEnd - gapStart) - sizeHint; if (lowWatermark <= size && size <= highWatermark) return; } else if ((position + sizeHint == gapStart) && (sizeHint < 0)) { // text is being deleted at the gap position int size = (gapEnd - gapStart) - sizeHint; if (lowWatermark <= size && size <= highWatermark) return; } moveAndResizeGap(position, sizeHint, line);}/** * Calculates the indexes of each line in the text store. Assumes no gap exists. * Optimized to do less checking. */void indexLines(){ int start = 0; lineCount = 0; int textLength = textStore.length; int i; for (i=start; i<textLength; i++) { char ch = textStore[i]; if (ch == SWT.CR) { // see if the next character is a LF if (i + 1 < textLength) { ch = textStore[i+1]; if (ch == SWT.LF) { i++; } } addLineIndex(start, i - start + 1); start = i + 1; } else if (ch == SWT.LF) { addLineIndex(start, i - start + 1); start = i + 1; } } addLineIndex(start, i - start);}/** * Returns whether or not the given character is a line delimiter. Both CR and LF * are valid line delimiters. * <p> * * @param ch the character to test * @return true if ch is a delimiter, false otherwise */boolean isDelimiter(char ch) { if (ch == SWT.CR) return true; if (ch == SWT.LF) return true; return false;} /** * Determine whether or not the replace operation is valid. DefaultContent will not allow * the /r/n line delimiter to be split or partially deleted. * <p> * * @param start start offset of text to replace * @param replaceLength start offset of text to replace * @param newText start offset of text to replace */protected boolean isValidReplace(int start, int replaceLength, String newText){ if (replaceLength == 0) { // inserting text, see if the \r\n line delimiter is being split if (start == 0) return true; if (start == getCharCount()) return true; char before = getTextRange(start - 1, 1).charAt(0); if (before == '\r') { char after = getTextRange(start, 1).charAt(0); if (after == '\n') return false; } } else { // deleting text, see if part of a \r\n line delimiter is being deleted char startChar = getTextRange(start, 1).charAt(0); if (startChar == '\n') { // see if char before delete position is \r if (start != 0) { char before = getTextRange(start - 1, 1).charAt(0); if (before == '\r') return false; } } char endChar = getTextRange(start + replaceLength - 1, 1).charAt(0); if (endChar == '\r') { // see if char after delete position is \n if (start + replaceLength != getCharCount()) { char after = getTextRange(start + replaceLength, 1).charAt(0); if (after == '\n') return false; } } } return true;}/** * Calculates the indexes of each line of text in the given range. * <p> * * @param offset the logical start offset of the text lineate * @param length the length of the text to lineate, includes gap * @param numLines the number of lines to initially allocate for the line index array, * passed in for efficiency (the exact number of lines may be known) * @return a line indexes array where each line is identified by a start offset and * a length */int[][] indexLines(int offset, int length, int numLines){ int[][] indexedLines = new int[numLines][2]; int start = 0; int lineCnt = 0; int i; replaceExpandExp = 1; for (i=start; i<length; i++) { int location = i + offset; if ((location >= gapStart) && (location < gapEnd)) { // ignore the gap } else { char ch = textStore[location]; if (ch == SWT.CR) { // see if the next character is a LF if (location+1 < textStore.length) { ch = textStore[location+1]; if (ch == SWT.LF) { i++; } } indexedLines = addLineIndex(start, i - start + 1, indexedLines, lineCnt); lineCnt++; start = i + 1; } else if (ch == SWT.LF) { indexedLines = addLineIndex(start, i - start + 1, indexedLines, lineCnt); lineCnt++; start = i + 1; } } } int[][] newLines = new int[lineCnt+1][2]; System.arraycopy(indexedLines, 0, newLines, 0, lineCnt); int[] range = new int[] {start, i - start}; newLines[lineCnt]=range; return newLines; }/** * Inserts text. * <p> * * @param position the position at which to insert the text * @param length the text to insert */void insert(int position, String text) { if (text.length() == 0) return; int startLine = getLineAtOffset(position); int change = text.length(); boolean endInsert = position == getCharCount(); adjustGap(position, change, startLine); // during an insert the gap will be adjusted to start at // position and it will be associated with startline, the // inserted text will be placed in the gap int startLineOffset = getOffsetAtLine(startLine); // at this point, startLineLength will include the start line // and all of the newly inserted text int startLineLength = getPhysicalLine(startLine).length(); if (change > 0) { // shrink gap gapStart += (change); for (int i = 0; i < text.length(); i++) textStore[position + i]= text.charAt(i); } // figure out the number of new lines that have been inserted int [][] newLines = indexLines(startLineOffset, startLineLength, 10); // only insert an empty line if it is the last line in the text int numNewLines = newLines.length - 1; if (newLines[numNewLines][1] == 0) { // last inserted line is a new line if (endInsert) { // insert happening at end of the text, leave numNewLines as // is since the last new line will not be concatenated with another // line numNewLines += 1; } else { numNewLines -= 1; } } // make room for the new lines expandLinesBy(numNewLines); // shift down the lines after the replace line for (int i = lineCount-1; i > startLine; i--) { lines[i + numNewLines]=lines[i]; } // insert the new lines for (int i=0; i<numNewLines; i++) { newLines[i][0] += startLineOffset; lines[startLine + i]=newLines[i]; } // update the last inserted line if (numNewLines < newLines.length) { newLines[numNewLines][0] += startLineOffset; lines[startLine + numNewLines] = newLines[numNewLines]; } lineCount += numNewLines; gapLine = getLineAtPhysicalOffset(gapStart); }/** * Moves the gap and adjusts its size in anticipation of a text change. * The gap is resized to actual size + the specified size and moved to the given * position. * <p> * * @param position the position at which a change is occurring * @param sizeHint the size of the change * @param line the line where the gap should be put */void moveAndResizeGap(int position, int size, int newGapLine) { char[] content = null; int oldSize = gapEnd - gapStart; int newSize; if (size > 0) { newSize = highWatermark + size; } else { newSize = lowWatermark - size; } // remove the old gap from the lines information if (gapExists()) { // adjust the line length lines[gapLine][1] = lines[gapLine][1] - oldSize; // adjust the offsets of the lines after the gapLine for (int i=gapLine+1; i<lineCount; i++) { lines[i][0]=lines[i][0]-oldSize; } } if (newSize < 0) { if (oldSize > 0) { // removing the gap content = new char[textStore.length - oldSize]; System.arraycopy(textStore, 0, content, 0, gapStart); System.arraycopy(textStore, gapEnd, content, gapStart, content.length - gapStart); textStore= content; } gapStart = gapEnd= position; return; } content = new char[textStore.length + (newSize - oldSize)]; int newGapStart = position; int newGapEnd = newGapStart + newSize; if (oldSize == 0) { System.arraycopy(textStore, 0, content, 0, newGapStart); System.arraycopy(textStore, newGapStart, content, newGapEnd, content.length - newGapEnd); } else if (newGapStart < gapStart) { int delta = gapStart - newGapStart; System.arraycopy(textStore, 0, content, 0, newGapStart); System.arraycopy(textStore, newGapStart, content, newGapEnd, delta); System.arraycopy(textStore, gapEnd, content, newGapEnd + delta, textStore.length - gapEnd); } else { int delta = newGapStart - gapStart; System.arraycopy(textStore, 0, content, 0, gapStart); System.arraycopy(textStore, gapEnd, content, gapStart, delta); System.arraycopy(textStore, gapEnd + delta, content, newGapEnd, content.length - newGapEnd); } textStore = content; gapStart = newGapStart; gapEnd = newGapEnd; // add the new gap to the lines information if (gapExists()) { gapLine = newGapLine; // adjust the line length int gapLength = gapEnd - gapStart; lines[gapLine][1] = lines[gapLine][1] + (gapLength); // adjust the offsets of the lines after the gapLine for (int i=gapLine+1; i<lineCount; i++) { lines[i][0]=lines[i][0]+gapLength; } }}/** * Returns the number of lines that are in the specified text. * <p> * * @param startOffset the start of the text to lineate * @param length the length of the text to lineate * @return number of lines */int lineCount(int startOffset, int length){ if (length == 0) { return 0; } int lineCnt = 0; int count = 0; int i = startOffset; if (i >= gapStart) { i += gapEnd - gapStart; } while (count < length) { if ((i >= gapStart) && (i < gapEnd)) { // ignore the gap } else { char ch = textStore[i]; if (ch == SWT.CR) { // see if the next character is a LF if (i + 1 < textStore.length) { ch = textStore[i+1]; if (ch == SWT.LF) { i++; count++; } } lineCnt++; } else if (ch == SWT.LF) { lineCnt++; } count++; } i++; } return lineCnt;}/** * Returns the number of lines that are in the specified text. * <p> * * @param text the text to lineate * @return number of lines in the text
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -