diff options
Diffstat (limited to 'bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultLineStyler.java')
-rwxr-xr-x | bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultLineStyler.java | 1354 |
1 files changed, 677 insertions, 677 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultLineStyler.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultLineStyler.java index 19224bd3cf..962386b47f 100755 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultLineStyler.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultLineStyler.java @@ -1,681 +1,681 @@ -package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom; +/* * Copyright (c) 2000, 2002 IBM Corp. All rights reserved. * This file is 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
- */
-
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.internal.Compatibility;
-import java.util.Vector;
-
-class DefaultLineStyler implements LineStyleListener, LineBackgroundListener {
- StyledTextContent content;
- StyleRange styles[] = new StyleRange[0];
- int styleCount = 0; // the number of styles
- int styleExpandExp = 1; // the expansion exponent, used to increase the styles array exponentially
- int lineExpandExp = 1; // the expansion exponent, used to increase the lines array exponentially
- int lineCount = 0;
- Color lineBackgrounds[];
-
-/**
- * Creates a new default line styler.
- * <p>
- *
- * @param content the text to which the styles apply
- */
-public DefaultLineStyler(StyledTextContent content) {
- this.content = content;
- lineCount = content.getLineCount();
- lineBackgrounds = new Color[lineCount];
-}
-/**
- * Inserts a style at the given location.
- * <p>
- *
- * @param style the new style
- * @param index the index at which to insert the style (the new style
- * will reside at this index)
- *
- */
-void insertStyle(StyleRange style, int index) {
- insertStyles(new StyleRange[] {style}, index);
-}
-/**
- * Insert the styles at the given location.
- * <p>
- *
- * @param insertStyles the new styles
- * @param index the index at which to insert the styles (the first new style
- * will reside at this index)
- *
- */
-void insertStyles(StyleRange[] insertStyles, int index) {
- int size = styles.length;
- int insertCount = insertStyles.length;
- int spaceNeeded = styleCount + insertCount - size;
- if (spaceNeeded > 0) {
- StyleRange[] newStyles = new StyleRange[size+spaceNeeded];
- System.arraycopy(styles, 0, newStyles, 0, size);
- styles = newStyles;
- }
- // shift the styles down to make room for the new styles
- System.arraycopy(styles, index, styles, index + insertCount, styleCount - index);
- // add the new styles
- System.arraycopy(insertStyles, 0, styles, index, insertCount);
- styleCount = styleCount + insertCount;
-}
-/**
- * Inserts a style, merging it with adjacent styles if possible.
- * <p>
- *
- * @param style the new style
- * @param index the index at which to insert the style (the new style
- * will reside at this index)
- * @return true if the style was inserted, false if the style was merged with an adjacent
- * style
- */
-boolean insertMergeStyle(StyleRange style, int index) {
- if (mergeStyleBefore(style, index)) return false;
- if (mergeStyleAfter(style, index)) return false;
- insertStyle(style, index);
- return true;
-}
-/**
- * Merges the style with the style before it if possible.
- * <p>
- *
- * @param style the new style
- * @param index the index at which to attempt the merge.
- * @return true if the style was merged, false otherwise
- */
-boolean mergeStyleBefore(StyleRange style, int index) {
- // see if the style is similar to the style before it and merge the
- // styles if possible
- if (index > 0) {
- StyleRange previous = styles[index-1];
- if (style.similarTo(previous)) {
- // the start of style needs to be in the range of the previous style
- // and the end of style needs to be < the start of the next style
- int previousEnd = previous.start + previous.length;
- if ((style.start <= previousEnd) && (style.start >= previous.start)) {
- int styleEnd = style.start + style.length;
- if ((index == styleCount) || (styleEnd <= styles[index].start)) {
- previous.length = style.start + style.length - previous.start;
- return true;
- }
- }
- }
- }
- return false;
-}
-/**
- * Merges the style with the style after it if possible.
- * <p>
- *
- * @param style the new style
- * @param index the index at which to attempt the merge.
- * @return true if the style was merged, false otherwise
- */
-boolean mergeStyleAfter(StyleRange style, int index) {
- // see if the style is similar to the style that will be after it and
- // merge the styles if possible
- if (index < styleCount) {
- StyleRange next = styles[index];
- if (style.similarTo(next)) {
- // the end of style needs to be in the range of the next style and
- // the start of style needs to be > the end of the previous style
- int styleEnd = style.start + style.length;
- int nextEnd = next.start + next.length;
- if ((styleEnd <= nextEnd) && (styleEnd >= next.start)) {
- if ((index == 0) || (style.start >= styles[index-1].start + styles[index-1].length)) {
- next.length = next.start + next.length - style.start;
- next.start = style.start;
- return true;
- }
- }
- }
- }
- return false;
-}
-/**
- * Removes style information that is defined for the range of text in <code>clearStyle</code>.
- * <p>
- *
- * @param clearStyle the style information to use for clearing
- */
-void clearStyle(StyleRange clearStyle) {
- Point pt = getOverlappingStyles(clearStyle.start, clearStyle.length);
- int clearStyleEnd = clearStyle.start + clearStyle.length - 1;
-
- // no overlapped styles exist
- if ((pt == null) || (pt.y == 0)) return;
-
- // the newStyle overlaps one or more of the existing styles
- // pt.x is the index of the first overlapped style, pt.y is the number of overlapped
- // styles
- int count = 0;
- int deleteStyle = -1;
- int deleteCount = 0;
- for (int i=pt.x; count<pt.y; i++) {
- StyleRange overlap = styles[i];
- int overlapEnd = overlap.start + overlap.length - 1;
- if (overlap.start < clearStyle.start) {
- if (overlapEnd <= clearStyleEnd) {
- // the end of overlap needs to be cleared
- overlap.length=clearStyle.start - overlap.start;
- } else {
- // middle of overlap needs to be cleared, this will
- // cause overlap to be broken into two
- StyleRange endStyle = (StyleRange)overlap.clone();
- endStyle.start = clearStyleEnd + 1;
- endStyle.length = overlapEnd - clearStyleEnd;
- overlap.length = clearStyle.start - overlap.start;
- insertStyle(endStyle, i+1);
- break;
- }
- } else {
- if (overlapEnd <= clearStyleEnd) {
- // entire overlap needs to be cleared
- if (deleteStyle == -1) {
- deleteStyle = i;
- }
- deleteCount++;
- } else {
- // beginning of overlap needs to be cleared
- overlap.start=clearStyleEnd + 1;
- overlap.length=overlapEnd - overlap.start + 1;
- break;
- }
- }
- count++;
- }
- deleteStyles(deleteStyle, deleteCount);
-}
-/**
- * Increases the <code>linebackgrounds</code> array to accomodate new line background
- * information.
- * <p>
- *
- * @param numLines the number to increase the array by
- */
-void expandLinesBy(int numLines) {
- int size = lineBackgrounds.length;
- if (size - lineCount >= numLines) {
- return;
- }
- Color[] newLines = new Color[size+Math.max(Compatibility.pow2(lineExpandExp), numLines)];
- System.arraycopy(lineBackgrounds, 0, newLines, 0, size);
- lineBackgrounds = newLines;
- lineExpandExp++;
-}
-/**
- * Deletes the style at <code>index</code>.
- * <p>
- *
- * @param index the index of the style to be deleted
- */
-void deleteStyle(int index) {
- deleteStyles(index, 1);
-}
-/**
- * Delete count styles starting at <code>index</code>.
- * <p>
- *
- * @param index the index of the style to be deleted
- * @param count the number of styles to be deleted
- */
-void deleteStyles(int index, int count) {
- if ((count == 0) || (index < 0)) return;
- // shift the styles up
- System.arraycopy(styles, index + count, styles, index, styleCount - (index + count));
- for (int i=0; i<count; i++) {
- styles[styleCount - i - 1] = null;
- }
- styleCount = styleCount - count;
-}
-/**
- * Returns the styles that are defined.
- * <p>
- *
- * @return the copied array of styles
- */
-StyleRange [] getStyleRanges() {
- StyleRange[] newStyles = new StyleRange[styleCount];
- System.arraycopy(styles, 0, newStyles, 0, styleCount);
- return newStyles;
-}
-/**
- * Handles the get line background color callback.
- * <p>
- *
- * @param event.lineOffset line number (input)
- * @param event.lineText line text (input)
- * @param event.background line background color (output)
- */
-public void lineGetBackground(LineBackgroundEvent event) {
- int lineIndex = content.getLineAtOffset(event.lineOffset);
- event.lineBackground = lineBackgrounds[lineIndex];
-}
-/**
- * Handles the get line style information callback.
- * <p>
- *
- * @param event.lineOffset line number (input)
- * @param event.lineText line text (input)
- * @param event.styles array of StyleRanges, need to be in order (output)
- */
-public void lineGetStyle(LineStyleEvent event) {
- int lineStart = event.lineOffset;
- int lineEnd = lineStart + event.lineText.length();
-
- int high = searchForStyle(lineStart, lineEnd);
- StyleRange style = null;
- Vector lineStyles = new Vector();
-
- // index will represent a style that
- // -- starts after the line (end processing)
- // -- ends before the line (continue processing)
- // -- starts before the line, ends in the line (add range)
- // -- starts in the line, ends in the line (add range)
- // -- starts in the line, ends after the line (add range)
- // -- starts before the line, ends after the line (add range)
- for (int index = high; index < styleCount; index++) {
- style = styles[index];
- if (style.start > lineEnd)
- // style starts after the line, end looping
- break;
- int styleEnd = style.start + style.length - 1;
- if (styleEnd >= lineStart) lineStyles.addElement(style);
- }
- event.styles = new StyleRange[lineStyles.size()];
- lineStyles.copyInto(event.styles);
-}
-/**
- * Searches for the first style in the <code>start</code> - <code>end</code> range.
- * <p>
- *
- * @return the index of the first style that overlaps the input range
- */
-int searchForStyle(int start, int end) {
- int high = styleCount;
- int low = -1;
- int index = high;
- // find the index of the first style for the given range, use a binary search
- while (high - low > 1) {
- index = (high + low) / 2;
- StyleRange style = styles[index];
- int styleEnd = style.start + style.length - 1;
- if (start <= style.start || end <= styleEnd || (start > style.start && styleEnd >= start && styleEnd < end)) {
- high = index;
- }
- else {
- low = index;
- }
- }
- return high;
-}
-/**
- * Updates the line background colors to reflect a new color. Called by StyledText.
- * <p>
- *
- * @param startLine index of the first line to color
- * @param lineCount number of lines to color starting at startLine
- * @param background the background color for the lines
- */
-void setLineBackground(int startLine, int count, Color background) {
- for (int i=startLine; i<startLine + count; i++) {
- lineBackgrounds[i]=background;
- }
-}
-/**
- * Update the styles to reflect the new style. <code>newStyle</code> will
- * replace any old style for the range. When this method is called, the
- * DefaultLineStyler may merge the new style with an existing style (if possible).
- * Called by StyledText when a style is added. Called by StyledText.
- * <p>
- *
- * @param newStyle the new style information.
- */
-void setStyleRange(StyleRange newStyle) {
- if (newStyle == null) {
- styles = new StyleRange[0];
- styleExpandExp = 1;
- styleCount = 0;
- return;
- }
- if (newStyle.length ==0) return;
- if (newStyle.isUnstyled()) {
- clearStyle(newStyle);
- return;
- }
-
- Point pt = getOverlappingStyles(newStyle.start, newStyle.length);
- int newStyleEnd = newStyle.start + newStyle.length - 1;
-
- // no styles exist
- if (pt == null) {
- insertStyle(newStyle, 0);
- return;
- }
-
- // newStyle does not overlap any other styles
- if (pt.y == 0) {
- insertMergeStyle(newStyle, pt.x);
- return;
- }
-
- // the newStyle overlaps one or more of the existing styles
- boolean added = false; // indicates whether or not the new style has been added
- int count = 0;
- // pt.x is the index of the first overlapped style, pt.y is the number of overlapped
- // styles
- for (int i=pt.x; count<pt.y; i++) {
- StyleRange overlap = styles[i];
- int overlapEnd = overlap.start + overlap.length - 1;
- if (overlap.start < newStyle.start) {
- if (overlapEnd <= newStyleEnd) {
- // the end of overlap needs to be replaced by newStyle
- if (newStyle.similarTo(overlap)) {
- // update overlap to accomodate the new style
- overlap.length = newStyle.start + newStyle.length - overlap.start;
- } else {
- overlap.length=newStyle.start - overlap.start;
- // see if newStyle can be merged with the style after overlap, if so,
- // processing is done
- if (mergeStyleAfter(newStyle, i+1)) break;
- // otherwise, insert the newStyle, newStyle may still overlap other
- // styles after it so continue processing
- insertStyle(newStyle, i+1);
- i++;
- }
- added = true;
- } else {
- // middle of overlap needs to be replaced by newStyle, this will
- // cause overlap to be broken into two
- if (newStyle.similarTo(overlap)) break;
- StyleRange endStyle = (StyleRange)overlap.clone();
- endStyle.start = newStyleEnd + 1;
- endStyle.length = overlapEnd - newStyleEnd;
- overlap.length = newStyle.start - overlap.start;
- insertStyle(newStyle, i+1);
- i++;
- insertStyle(endStyle, i+1);
- // when newStyle overlaps the middle of a style, this implies that
- // processing is done (no more overlapped styles)
- break;
- }
- } else {
- if (overlapEnd <= newStyleEnd) {
- // overlap will be replaced by the newStyle, make sure newStyle
- // hasn't already been added, if it has just delete overlap
- if (!added) {
- styles[i] = newStyle;
- added = true;
- } else {
- deleteStyle(i);
- i--;
- }
- } else {
- // beginning of overlap needs to be replaced by newStyle
- overlap.start=newStyleEnd + 1;
- overlap.length=overlapEnd - overlap.start + 1;
- if (!added) {
- insertMergeStyle(newStyle, i);
- }
- // when newStyle overlaps only the beginning of a style, this implies
- // that processing is done (no more overlapped styles)
- break;
- }
- }
- count++;
- }
-}
-/**
- * Replace the styles for the given range.
- * <p>
- *
- * @param styles the new styles, must be in order and non-overlapping
- */
-void replaceStyleRanges(int start, int length, StyleRange[] ranges) {
- clearStyle(new StyleRange(start, length, null, null));
- // find insert point
- int high = styleCount;
- int low = -1;
- int index = high;
- while (high - low > 1) {
- index = (high + low) / 2;
- StyleRange style = styles[index];
- if (start <= style.start) {
- high = index;
- }
- else {
- low = index;
- }
- }
- insertStyles(ranges, high);
-}
-/**
- * Sets the array of styles and discards old styles. Called by StyledText.
- * <p>
- *
- * @param styles the new styles, must be in order and non-overlapping
- */
-void setStyleRanges(StyleRange[] styles) {
- this.styles = new StyleRange[styles.length];
- System.arraycopy(styles, 0, this.styles, 0, styles.length);
- styleCount = styles.length;
- styleExpandExp = 1;
-}
-/**
- * Updates the style ranges and line backgrounds to reflect a pending text
- * change.
- * Called by StyledText when a TextChangingEvent is received.
- * <p>
- *
- * @param event the event with the text change information
- */
-public void textChanging(TextChangingEvent event) {
- int startLine = content.getLineAtOffset(event.start);
- int startLineOffset = content.getOffsetAtLine(startLine);
-
- textChanging(event.start, -event.replaceCharCount);
- textChanging(event.start, event.newCharCount);
-
- if (event.replaceCharCount == content.getCharCount()) {
- // all text is going to be replaced, clear line backgrounds
- linesChanging(0, -lineCount);
- linesChanging(0, content.getLineCount() - event.replaceLineCount + event.newLineCount);
- return;
- }
-
- if (event.start != startLineOffset) {
- startLine = startLine + 1;
- }
-
- linesChanging(startLine, -event.replaceLineCount);
- linesChanging(startLine, event.newLineCount);
-}
-/*
- * Updates the line backgrounds to reflect a pending text change.
- * <p>
- *
- * @param start the starting line of the change that is about to take place
- * @param delta the number of lines in the change, > 0 indicates lines inserted,
- * < 0 indicates lines deleted
- */
-void linesChanging(int start, int delta) {
- if (delta == 0) return;
- boolean inserting = delta > 0;
- if (inserting) {
- // shift the lines down to make room for new lines
- expandLinesBy(delta);
- for (int i = lineCount-1; i >= start; i--) {
- lineBackgrounds[i + delta]=lineBackgrounds[i];
- }
- for (int i=start; i<start + delta; i++) {
- lineBackgrounds[i]=null;
- }
- } else {
- // shift up the lines
- for (int i = start - delta; i < lineCount; i++) {
- lineBackgrounds[i+delta]=lineBackgrounds[i];
- }
- }
- lineCount += delta;
-}
-/*
- * Updates the style ranges to reflect a text change.
- * <p>
- *
- * @param start the starting offset of the change that is about to
- * take place
- * @param delta the length of the change, > 0 indicates text inserted,
- * < 0 indicates text deleted
- */
-void textChanging(int start, int delta) {
- if (delta == 0) return;
- StyleRange style;
- // find the index of the first style for the given offset, use a binary search
- // to find the index
- int end;
- int deleteStart = -1;
- int deleteCount = 0;
- boolean inserting = delta > 0;
- if (inserting) {
- end = (start + delta) - 1;
- } else {
- end = (start - delta) - 1;
- }
- int high = searchForStyle(start, end);
- int index;
- // update the styles that are in the affected range
- for (index = high; index < styleCount; index++) {
- style = styles[index];
- if (inserting) {
- if (style.start >= start) break;
- // in the insert case only one style range will be directly affected,
- // it will need to be split into two and then the newStyle inserted
- StyleRange beforeStyle = (StyleRange)style.clone();
- beforeStyle.length = start - style.start;
- style.start = start;
- style.length = style.length - beforeStyle.length;
- if (beforeStyle.length != 0) insertStyle(beforeStyle, index);
- index++;
- break;
- } else {
- int styleEnd = style.start + style.length - 1;
- if (style.start > end) break;
- // in the delete case, any style that overlaps the change range will be
- // affected
- if (style.start < start) {
- if (styleEnd <= end) {
- // style starts before change range, ends in change range
- style.length = start - style.start;
- } else {
- // style starts before change range, ends after change range
- style.length = style.length + delta;
- index++;
- break;
- }
- } else {
- if (styleEnd <= end) {
- // style starts in change range, ends in change range
- if (deleteStart == -1) {
- deleteStart = index;
- }
- deleteCount++;
- } else {
- // style starts in change range, ends after change range
- style.start = start;
- style.length = styleEnd - end;
- index++;
- break;
- }
- }
- }
- }
- deleteStyles(deleteStart, deleteCount);
- // change the offsets of the styles after the affected styles
- for (int i = index - deleteCount; i < styleCount; i++) {
- style = styles[i];
- style.start = style.start + delta;
- }
-}
-/**
- * Returns the indexes of the styles that overlap the given range. Styles that partially
- * or fully overlap the range will be returned.
- * <p>
- *
- * @return Point where x is the index of the starting overlap style, y is the number of
- * styles that overlap the range
- */
-Point getOverlappingStyles(int start, int length) {
- StyleRange style;
- if (styleCount == 0) return null;
- // find the index of the first style for the given offset, use a binary search
- // to find the index
- int end = start + length - 1;
- int high = searchForStyle(start, end);
- int count = 0;
- for (int index = high; index < styleCount; index++) {
- style = styles[index];
- int styleEnd = style.start + style.length - 1;
- if (style.start > end) break;
- if (styleEnd >= start) count++;
- }
- return new Point(high, count);
-}
-/**
- * Returns the background color of a line. Called by StyledText. It is safe to return
- * the existing Color object since the colors are set and managed by the client.
- * <p>
- *
- * @param index the line index
- * @return the background color of the line at the given index
- */
-Color getLineBackground(int index) {
- return lineBackgrounds[index];
-}
-/**
- * Returns the style for the character at <code>offset</code>. Called by StyledText.
- * Returns a new style. Does not return the existing style.
- * <p>
- *
- * @param offset the character position in the text
- * @return a cloned StyleRange with start == offset and length == 1 if a style is
- * specified or null if no style is specified
- */
-StyleRange getStyleRangeAtOffset(int offset) {
- if (styleCount == 0) return null;
- Point pt = getOverlappingStyles(offset, 1);
- if (pt == null || pt.y == 0) return null;
- StyleRange newStyle = (StyleRange)styles[pt.x].clone();
- newStyle.start = offset;
- newStyle.length = 1;
- return newStyle;
-}
-/**
- * Returns the styles for the given range. Returns the existing styles,
- * so be careful not to modify the return value. Styles are not cloned
- * in order to make this method as efficient as possible.
- * <p>
- *
- * @param offset the start position of the text range
- * @param length the length of the text range
- * @return a StyleRange array or null if no styles are specified for the text
- * range
- */
-StyleRange[] getStyleRangesFor(int offset, int length) {
- if (styleCount == 0) return null;
- Point pt = getOverlappingStyles(offset, length);
- if (pt == null || pt.y == 0) return null;
- StyleRange[] ranges = new StyleRange[pt.y];
- for (int i=0; i<pt.y; i++) {
- StyleRange newStyle = styles[pt.x + i];
- ranges[i]=newStyle;
- }
- return ranges;
-}
+ * http://www.eclipse.org/legal/cpl-v10.html + */ + +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.Compatibility; +import java.util.Vector; + +class DefaultLineStyler implements LineStyleListener, LineBackgroundListener { + StyledTextContent content; + StyleRange styles[] = new StyleRange[0]; + int styleCount = 0; // the number of styles + int styleExpandExp = 1; // the expansion exponent, used to increase the styles array exponentially + int lineExpandExp = 1; // the expansion exponent, used to increase the lines array exponentially + int lineCount = 0; + Color lineBackgrounds[]; + +/** + * Creates a new default line styler. + * <p> + * + * @param content the text to which the styles apply + */ +public DefaultLineStyler(StyledTextContent content) { + this.content = content; + lineCount = content.getLineCount(); + lineBackgrounds = new Color[lineCount]; +} +/** + * Inserts a style at the given location. + * <p> + * + * @param style the new style + * @param index the index at which to insert the style (the new style + * will reside at this index) + * + */ +void insertStyle(StyleRange style, int index) { + insertStyles(new StyleRange[] {style}, index); +} +/** + * Insert the styles at the given location. + * <p> + * + * @param insertStyles the new styles + * @param index the index at which to insert the styles (the first new style + * will reside at this index) + * + */ +void insertStyles(StyleRange[] insertStyles, int index) { + int size = styles.length; + int insertCount = insertStyles.length; + int spaceNeeded = styleCount + insertCount - size; + if (spaceNeeded > 0) { + StyleRange[] newStyles = new StyleRange[size+spaceNeeded]; + System.arraycopy(styles, 0, newStyles, 0, size); + styles = newStyles; + } + // shift the styles down to make room for the new styles + System.arraycopy(styles, index, styles, index + insertCount, styleCount - index); + // add the new styles + System.arraycopy(insertStyles, 0, styles, index, insertCount); + styleCount = styleCount + insertCount; +} +/** + * Inserts a style, merging it with adjacent styles if possible. + * <p> + * + * @param style the new style + * @param index the index at which to insert the style (the new style + * will reside at this index) + * @return true if the style was inserted, false if the style was merged with an adjacent + * style + */ +boolean insertMergeStyle(StyleRange style, int index) { + if (mergeStyleBefore(style, index)) return false; + if (mergeStyleAfter(style, index)) return false; + insertStyle(style, index); + return true; +} +/** + * Merges the style with the style before it if possible. + * <p> + * + * @param style the new style + * @param index the index at which to attempt the merge. + * @return true if the style was merged, false otherwise + */ +boolean mergeStyleBefore(StyleRange style, int index) { + // see if the style is similar to the style before it and merge the + // styles if possible + if (index > 0) { + StyleRange previous = styles[index-1]; + if (style.similarTo(previous)) { + // the start of style needs to be in the range of the previous style + // and the end of style needs to be < the start of the next style + int previousEnd = previous.start + previous.length; + if ((style.start <= previousEnd) && (style.start >= previous.start)) { + int styleEnd = style.start + style.length; + if ((index == styleCount) || (styleEnd <= styles[index].start)) { + previous.length = style.start + style.length - previous.start; + return true; + } + } + } + } + return false; +} +/** + * Merges the style with the style after it if possible. + * <p> + * + * @param style the new style + * @param index the index at which to attempt the merge. + * @return true if the style was merged, false otherwise + */ +boolean mergeStyleAfter(StyleRange style, int index) { + // see if the style is similar to the style that will be after it and + // merge the styles if possible + if (index < styleCount) { + StyleRange next = styles[index]; + if (style.similarTo(next)) { + // the end of style needs to be in the range of the next style and + // the start of style needs to be > the end of the previous style + int styleEnd = style.start + style.length; + int nextEnd = next.start + next.length; + if ((styleEnd <= nextEnd) && (styleEnd >= next.start)) { + if ((index == 0) || (style.start >= styles[index-1].start + styles[index-1].length)) { + next.length = next.start + next.length - style.start; + next.start = style.start; + return true; + } + } + } + } + return false; +} +/** + * Removes style information that is defined for the range of text in <code>clearStyle</code>. + * <p> + * + * @param clearStyle the style information to use for clearing + */ +void clearStyle(StyleRange clearStyle) { + Point pt = getOverlappingStyles(clearStyle.start, clearStyle.length); + int clearStyleEnd = clearStyle.start + clearStyle.length - 1; + + // no overlapped styles exist + if ((pt == null) || (pt.y == 0)) return; + + // the newStyle overlaps one or more of the existing styles + // pt.x is the index of the first overlapped style, pt.y is the number of overlapped + // styles + int count = 0; + int deleteStyle = -1; + int deleteCount = 0; + for (int i=pt.x; count<pt.y; i++) { + StyleRange overlap = styles[i]; + int overlapEnd = overlap.start + overlap.length - 1; + if (overlap.start < clearStyle.start) { + if (overlapEnd <= clearStyleEnd) { + // the end of overlap needs to be cleared + overlap.length=clearStyle.start - overlap.start; + } else { + // middle of overlap needs to be cleared, this will + // cause overlap to be broken into two + StyleRange endStyle = (StyleRange)overlap.clone(); + endStyle.start = clearStyleEnd + 1; + endStyle.length = overlapEnd - clearStyleEnd; + overlap.length = clearStyle.start - overlap.start; + insertStyle(endStyle, i+1); + break; + } + } else { + if (overlapEnd <= clearStyleEnd) { + // entire overlap needs to be cleared + if (deleteStyle == -1) { + deleteStyle = i; + } + deleteCount++; + } else { + // beginning of overlap needs to be cleared + overlap.start=clearStyleEnd + 1; + overlap.length=overlapEnd - overlap.start + 1; + break; + } + } + count++; + } + deleteStyles(deleteStyle, deleteCount); +} +/** + * Increases the <code>linebackgrounds</code> array to accomodate new line background + * information. + * <p> + * + * @param numLines the number to increase the array by + */ +void expandLinesBy(int numLines) { + int size = lineBackgrounds.length; + if (size - lineCount >= numLines) { + return; + } + Color[] newLines = new Color[size+Math.max(Compatibility.pow2(lineExpandExp), numLines)]; + System.arraycopy(lineBackgrounds, 0, newLines, 0, size); + lineBackgrounds = newLines; + lineExpandExp++; +} +/** + * Deletes the style at <code>index</code>. + * <p> + * + * @param index the index of the style to be deleted + */ +void deleteStyle(int index) { + deleteStyles(index, 1); +} +/** + * Delete count styles starting at <code>index</code>. + * <p> + * + * @param index the index of the style to be deleted + * @param count the number of styles to be deleted + */ +void deleteStyles(int index, int count) { + if ((count == 0) || (index < 0)) return; + // shift the styles up + System.arraycopy(styles, index + count, styles, index, styleCount - (index + count)); + for (int i=0; i<count; i++) { + styles[styleCount - i - 1] = null; + } + styleCount = styleCount - count; +} +/** + * Returns the styles that are defined. + * <p> + * + * @return the copied array of styles + */ +StyleRange [] getStyleRanges() { + StyleRange[] newStyles = new StyleRange[styleCount]; + System.arraycopy(styles, 0, newStyles, 0, styleCount); + return newStyles; +} +/** + * Handles the get line background color callback. + * <p> + * + * @param event.lineOffset line number (input) + * @param event.lineText line text (input) + * @param event.background line background color (output) + */ +public void lineGetBackground(LineBackgroundEvent event) { + int lineIndex = content.getLineAtOffset(event.lineOffset); + event.lineBackground = lineBackgrounds[lineIndex]; +} +/** + * Handles the get line style information callback. + * <p> + * + * @param event.lineOffset line number (input) + * @param event.lineText line text (input) + * @param event.styles array of StyleRanges, need to be in order (output) + */ +public void lineGetStyle(LineStyleEvent event) { + int lineStart = event.lineOffset; + int lineEnd = lineStart + event.lineText.length(); + + int high = searchForStyle(lineStart, lineEnd); + StyleRange style = null; + Vector lineStyles = new Vector(); + + // index will represent a style that + // -- starts after the line (end processing) + // -- ends before the line (continue processing) + // -- starts before the line, ends in the line (add range) + // -- starts in the line, ends in the line (add range) + // -- starts in the line, ends after the line (add range) + // -- starts before the line, ends after the line (add range) + for (int index = high; index < styleCount; index++) { + style = styles[index]; + if (style.start > lineEnd) + // style starts after the line, end looping + break; + int styleEnd = style.start + style.length - 1; + if (styleEnd >= lineStart) lineStyles.addElement(style); + } + event.styles = new StyleRange[lineStyles.size()]; + lineStyles.copyInto(event.styles); +} +/** + * Searches for the first style in the <code>start</code> - <code>end</code> range. + * <p> + * + * @return the index of the first style that overlaps the input range + */ +int searchForStyle(int start, int end) { + int high = styleCount; + int low = -1; + int index = high; + // find the index of the first style for the given range, use a binary search + while (high - low > 1) { + index = (high + low) / 2; + StyleRange style = styles[index]; + int styleEnd = style.start + style.length - 1; + if (start <= style.start || end <= styleEnd || (start > style.start && styleEnd >= start && styleEnd < end)) { + high = index; + } + else { + low = index; + } + } + return high; +} +/** + * Updates the line background colors to reflect a new color. Called by StyledText. + * <p> + * + * @param startLine index of the first line to color + * @param lineCount number of lines to color starting at startLine + * @param background the background color for the lines + */ +void setLineBackground(int startLine, int count, Color background) { + for (int i=startLine; i<startLine + count; i++) { + lineBackgrounds[i]=background; + } +} +/** + * Update the styles to reflect the new style. <code>newStyle</code> will + * replace any old style for the range. When this method is called, the + * DefaultLineStyler may merge the new style with an existing style (if possible). + * Called by StyledText when a style is added. Called by StyledText. + * <p> + * + * @param newStyle the new style information. + */ +void setStyleRange(StyleRange newStyle) { + if (newStyle == null) { + styles = new StyleRange[0]; + styleExpandExp = 1; + styleCount = 0; + return; + } + if (newStyle.length ==0) return; + if (newStyle.isUnstyled()) { + clearStyle(newStyle); + return; + } + + Point pt = getOverlappingStyles(newStyle.start, newStyle.length); + int newStyleEnd = newStyle.start + newStyle.length - 1; + + // no styles exist + if (pt == null) { + insertStyle(newStyle, 0); + return; + } + + // newStyle does not overlap any other styles + if (pt.y == 0) { + insertMergeStyle(newStyle, pt.x); + return; + } + + // the newStyle overlaps one or more of the existing styles + boolean added = false; // indicates whether or not the new style has been added + int count = 0; + // pt.x is the index of the first overlapped style, pt.y is the number of overlapped + // styles + for (int i=pt.x; count<pt.y; i++) { + StyleRange overlap = styles[i]; + int overlapEnd = overlap.start + overlap.length - 1; + if (overlap.start < newStyle.start) { + if (overlapEnd <= newStyleEnd) { + // the end of overlap needs to be replaced by newStyle + if (newStyle.similarTo(overlap)) { + // update overlap to accomodate the new style + overlap.length = newStyle.start + newStyle.length - overlap.start; + } else { + overlap.length=newStyle.start - overlap.start; + // see if newStyle can be merged with the style after overlap, if so, + // processing is done + if (mergeStyleAfter(newStyle, i+1)) break; + // otherwise, insert the newStyle, newStyle may still overlap other + // styles after it so continue processing + insertStyle(newStyle, i+1); + i++; + } + added = true; + } else { + // middle of overlap needs to be replaced by newStyle, this will + // cause overlap to be broken into two + if (newStyle.similarTo(overlap)) break; + StyleRange endStyle = (StyleRange)overlap.clone(); + endStyle.start = newStyleEnd + 1; + endStyle.length = overlapEnd - newStyleEnd; + overlap.length = newStyle.start - overlap.start; + insertStyle(newStyle, i+1); + i++; + insertStyle(endStyle, i+1); + // when newStyle overlaps the middle of a style, this implies that + // processing is done (no more overlapped styles) + break; + } + } else { + if (overlapEnd <= newStyleEnd) { + // overlap will be replaced by the newStyle, make sure newStyle + // hasn't already been added, if it has just delete overlap + if (!added) { + styles[i] = newStyle; + added = true; + } else { + deleteStyle(i); + i--; + } + } else { + // beginning of overlap needs to be replaced by newStyle + overlap.start=newStyleEnd + 1; + overlap.length=overlapEnd - overlap.start + 1; + if (!added) { + insertMergeStyle(newStyle, i); + } + // when newStyle overlaps only the beginning of a style, this implies + // that processing is done (no more overlapped styles) + break; + } + } + count++; + } +} +/** + * Replace the styles for the given range. + * <p> + * + * @param styles the new styles, must be in order and non-overlapping + */ +void replaceStyleRanges(int start, int length, StyleRange[] ranges) { + clearStyle(new StyleRange(start, length, null, null)); + // find insert point + int high = styleCount; + int low = -1; + int index = high; + while (high - low > 1) { + index = (high + low) / 2; + StyleRange style = styles[index]; + if (start <= style.start) { + high = index; + } + else { + low = index; + } + } + insertStyles(ranges, high); +} +/** + * Sets the array of styles and discards old styles. Called by StyledText. + * <p> + * + * @param styles the new styles, must be in order and non-overlapping + */ +void setStyleRanges(StyleRange[] styles) { + this.styles = new StyleRange[styles.length]; + System.arraycopy(styles, 0, this.styles, 0, styles.length); + styleCount = styles.length; + styleExpandExp = 1; +} +/** + * Updates the style ranges and line backgrounds to reflect a pending text + * change. + * Called by StyledText when a TextChangingEvent is received. + * <p> + * + * @param event the event with the text change information + */ +public void textChanging(TextChangingEvent event) { + int startLine = content.getLineAtOffset(event.start); + int startLineOffset = content.getOffsetAtLine(startLine); + + textChanging(event.start, -event.replaceCharCount); + textChanging(event.start, event.newCharCount); + + if (event.replaceCharCount == content.getCharCount()) { + // all text is going to be replaced, clear line backgrounds + linesChanging(0, -lineCount); + linesChanging(0, content.getLineCount() - event.replaceLineCount + event.newLineCount); + return; + } + + if (event.start != startLineOffset) { + startLine = startLine + 1; + } + + linesChanging(startLine, -event.replaceLineCount); + linesChanging(startLine, event.newLineCount); +} +/* + * Updates the line backgrounds to reflect a pending text change. + * <p> + * + * @param start the starting line of the change that is about to take place + * @param delta the number of lines in the change, > 0 indicates lines inserted, + * < 0 indicates lines deleted + */ +void linesChanging(int start, int delta) { + if (delta == 0) return; + boolean inserting = delta > 0; + if (inserting) { + // shift the lines down to make room for new lines + expandLinesBy(delta); + for (int i = lineCount-1; i >= start; i--) { + lineBackgrounds[i + delta]=lineBackgrounds[i]; + } + for (int i=start; i<start + delta; i++) { + lineBackgrounds[i]=null; + } + } else { + // shift up the lines + for (int i = start - delta; i < lineCount; i++) { + lineBackgrounds[i+delta]=lineBackgrounds[i]; + } + } + lineCount += delta; +} +/* + * Updates the style ranges to reflect a text change. + * <p> + * + * @param start the starting offset of the change that is about to + * take place + * @param delta the length of the change, > 0 indicates text inserted, + * < 0 indicates text deleted + */ +void textChanging(int start, int delta) { + if (delta == 0) return; + StyleRange style; + // find the index of the first style for the given offset, use a binary search + // to find the index + int end; + int deleteStart = -1; + int deleteCount = 0; + boolean inserting = delta > 0; + if (inserting) { + end = (start + delta) - 1; + } else { + end = (start - delta) - 1; + } + int high = searchForStyle(start, end); + int index; + // update the styles that are in the affected range + for (index = high; index < styleCount; index++) { + style = styles[index]; + if (inserting) { + if (style.start >= start) break; + // in the insert case only one style range will be directly affected, + // it will need to be split into two and then the newStyle inserted + StyleRange beforeStyle = (StyleRange)style.clone(); + beforeStyle.length = start - style.start; + style.start = start; + style.length = style.length - beforeStyle.length; + if (beforeStyle.length != 0) insertStyle(beforeStyle, index); + index++; + break; + } else { + int styleEnd = style.start + style.length - 1; + if (style.start > end) break; + // in the delete case, any style that overlaps the change range will be + // affected + if (style.start < start) { + if (styleEnd <= end) { + // style starts before change range, ends in change range + style.length = start - style.start; + } else { + // style starts before change range, ends after change range + style.length = style.length + delta; + index++; + break; + } + } else { + if (styleEnd <= end) { + // style starts in change range, ends in change range + if (deleteStart == -1) { + deleteStart = index; + } + deleteCount++; + } else { + // style starts in change range, ends after change range + style.start = start; + style.length = styleEnd - end; + index++; + break; + } + } + } + } + deleteStyles(deleteStart, deleteCount); + // change the offsets of the styles after the affected styles + for (int i = index - deleteCount; i < styleCount; i++) { + style = styles[i]; + style.start = style.start + delta; + } +} +/** + * Returns the indexes of the styles that overlap the given range. Styles that partially + * or fully overlap the range will be returned. + * <p> + * + * @return Point where x is the index of the starting overlap style, y is the number of + * styles that overlap the range + */ +Point getOverlappingStyles(int start, int length) { + StyleRange style; + if (styleCount == 0) return null; + // find the index of the first style for the given offset, use a binary search + // to find the index + int end = start + length - 1; + int high = searchForStyle(start, end); + int count = 0; + for (int index = high; index < styleCount; index++) { + style = styles[index]; + int styleEnd = style.start + style.length - 1; + if (style.start > end) break; + if (styleEnd >= start) count++; + } + return new Point(high, count); +} +/** + * Returns the background color of a line. Called by StyledText. It is safe to return + * the existing Color object since the colors are set and managed by the client. + * <p> + * + * @param index the line index + * @return the background color of the line at the given index + */ +Color getLineBackground(int index) { + return lineBackgrounds[index]; +} +/** + * Returns the style for the character at <code>offset</code>. Called by StyledText. + * Returns a new style. Does not return the existing style. + * <p> + * + * @param offset the character position in the text + * @return a cloned StyleRange with start == offset and length == 1 if a style is + * specified or null if no style is specified + */ +StyleRange getStyleRangeAtOffset(int offset) { + if (styleCount == 0) return null; + Point pt = getOverlappingStyles(offset, 1); + if (pt == null || pt.y == 0) return null; + StyleRange newStyle = (StyleRange)styles[pt.x].clone(); + newStyle.start = offset; + newStyle.length = 1; + return newStyle; +} +/** + * Returns the styles for the given range. Returns the existing styles, + * so be careful not to modify the return value. Styles are not cloned + * in order to make this method as efficient as possible. + * <p> + * + * @param offset the start position of the text range + * @param length the length of the text range + * @return a StyleRange array or null if no styles are specified for the text + * range + */ +StyleRange[] getStyleRangesFor(int offset, int length) { + if (styleCount == 0) return null; + Point pt = getOverlappingStyles(offset, length); + if (pt == null || pt.y == 0) return null; + StyleRange[] ranges = new StyleRange[pt.y]; + for (int i=0; i<pt.y; i++) { + StyleRange newStyle = styles[pt.x + i]; + ranges[i]=newStyle; + } + return ranges; +} } |