/******************************************************************************* * Copyright (c) 2009 Innoopract Informationssysteme GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Innoopract Informationssysteme GmbH - initial API and implementation ******************************************************************************/ package org.eclipse.swt.custom; import org.eclipse.swt.SWT; /** * A StyledTextRenderer renders the content of a StyledText widget. */ class StyledTextRenderer { StyledText styledText; int[] ranges; int styleCount; StyleRange[] styles; StyleRange[] stylesSet; int stylesSetCount = 0; final static boolean COMPACT_STYLES = true; final static boolean MERGE_STYLES = true; final static int GROW = 32; StyledTextRenderer( final StyledText styledText ) { this.styledText = styledText; } int addMerge( int[] mergeRanges, StyleRange[] mergeStyles, int mergeCount, int modifyStart, int modifyEnd ) { int rangeCount = styleCount << 1; StyleRange endStyle = null; int endStart = 0, endLength = 0; if( modifyEnd < rangeCount ) { endStyle = styles[ modifyEnd >> 1 ]; endStart = ranges[ modifyEnd ]; endLength = ranges[ modifyEnd + 1 ]; } int grow = mergeCount - ( modifyEnd - modifyStart ); if( rangeCount + grow >= ranges.length ) { int[] tmpRanges = new int[ ranges.length + grow + ( GROW << 1 ) ]; System.arraycopy( ranges, 0, tmpRanges, 0, modifyStart ); StyleRange[] tmpStyles = new StyleRange[ styles.length + ( grow >> 1 ) + GROW ]; System.arraycopy( styles, 0, tmpStyles, 0, modifyStart >> 1 ); if( rangeCount > modifyEnd ) { System.arraycopy( ranges, modifyEnd, tmpRanges, modifyStart + mergeCount, rangeCount - modifyEnd ); System.arraycopy( styles, modifyEnd >> 1, tmpStyles, ( modifyStart + mergeCount ) >> 1, styleCount - ( modifyEnd >> 1 ) ); } ranges = tmpRanges; styles = tmpStyles; } else { if( rangeCount > modifyEnd ) { System.arraycopy( ranges, modifyEnd, ranges, modifyStart + mergeCount, rangeCount - modifyEnd ); System.arraycopy( styles, modifyEnd >> 1, styles, ( modifyStart + mergeCount ) >> 1, styleCount - ( modifyEnd >> 1 ) ); } } if( MERGE_STYLES ) { int j = modifyStart; for( int i = 0; i < mergeCount; i += 2 ) { if( j > 0 && ranges[ j - 2 ] + ranges[ j - 1 ] == mergeRanges[ i ] && mergeStyles[ i >> 1 ].similarTo( styles[ ( j - 2 ) >> 1 ] ) ) { ranges[ j - 1 ] += mergeRanges[ i + 1 ]; } else { styles[ j >> 1 ] = mergeStyles[ i >> 1 ]; ranges[ j++ ] = mergeRanges[ i ]; ranges[ j++ ] = mergeRanges[ i + 1 ]; } } if( endStyle != null && ranges[ j - 2 ] + ranges[ j - 1 ] == endStart && endStyle.similarTo( styles[ ( j - 2 ) >> 1 ] ) ) { ranges[ j - 1 ] += endLength; modifyEnd += 2; mergeCount += 2; } if( rangeCount > modifyEnd ) { System.arraycopy( ranges, modifyStart + mergeCount, ranges, j, rangeCount - modifyEnd ); System.arraycopy( styles, ( modifyStart + mergeCount ) >> 1, styles, j >> 1, styleCount - ( modifyEnd >> 1 ) ); } grow = ( j - modifyStart ) - ( modifyEnd - modifyStart ); } else { System.arraycopy( mergeRanges, 0, ranges, modifyStart, mergeCount ); System.arraycopy( mergeStyles, 0, styles, modifyStart >> 1, mergeCount >> 1 ); } styleCount += grow >> 1; return grow; } int addMerge( StyleRange[] mergeStyles, int mergeCount, int modifyStart, int modifyEnd ) { int grow = mergeCount - ( modifyEnd - modifyStart ); StyleRange endStyle = null; if( modifyEnd < styleCount ) endStyle = styles[ modifyEnd ]; if( styleCount + grow >= styles.length ) { StyleRange[] tmpStyles = new StyleRange[ styles.length + grow + GROW ]; System.arraycopy( styles, 0, tmpStyles, 0, modifyStart ); if( styleCount > modifyEnd ) { System.arraycopy( styles, modifyEnd, tmpStyles, modifyStart + mergeCount, styleCount - modifyEnd ); } styles = tmpStyles; } else { if( styleCount > modifyEnd ) { System.arraycopy( styles, modifyEnd, styles, modifyStart + mergeCount, styleCount - modifyEnd ); } } if( MERGE_STYLES ) { int j = modifyStart; for( int i = 0; i < mergeCount; i++ ) { StyleRange newStyle = mergeStyles[ i ], style; if( j > 0 && ( style = styles[ j - 1 ] ).start + style.length == newStyle.start && newStyle.similarTo( style ) ) { style.length += newStyle.length; } else { styles[ j++ ] = newStyle; } } StyleRange style = styles[ j - 1 ]; if( endStyle != null && style.start + style.length == endStyle.start && endStyle.similarTo( style ) ) { style.length += endStyle.length; modifyEnd++; mergeCount++; } if( styleCount > modifyEnd ) { System.arraycopy( styles, modifyStart + mergeCount, styles, j, styleCount - modifyEnd ); } grow = ( j - modifyStart ) - ( modifyEnd - modifyStart ); } else { System.arraycopy( mergeStyles, 0, styles, modifyStart, mergeCount ); } styleCount += grow; return grow; } int getRangeIndex( int offset, int low, int high ) { if( styleCount == 0 ) return 0; if( ranges != null ) { while( high - low > 2 ) { int index = ( ( high + low ) / 2 ) / 2 * 2; int end = ranges[ index ] + ranges[ index + 1 ]; if( end > offset ) { high = index; } else { low = index; } } } else { while( high - low > 1 ) { int index = ( ( high + low ) / 2 ); int end = styles[ index ].start + styles[ index ].length; if( end > offset ) { high = index; } else { low = index; } } } return high; } int[] getRanges( int start, int length ) { int[] newRanges; int end = start + length - 1; if( ranges != null ) { int rangeCount = styleCount << 1; int rangeStart = getRangeIndex( start, -1, rangeCount ); if( rangeStart >= rangeCount ) return null; if( ranges[ rangeStart ] > end ) return null; int rangeEnd = Math.min( rangeCount - 2, getRangeIndex( end, rangeStart - 1, rangeCount ) + 1 ); newRanges = new int[ rangeEnd - rangeStart + 2 ]; System.arraycopy( ranges, rangeStart, newRanges, 0, newRanges.length ); } else { int rangeStart = getRangeIndex( start, -1, styleCount ); if( rangeStart >= styleCount ) return null; if( styles[ rangeStart ].start > end ) return null; int rangeEnd = Math.min( styleCount - 1, getRangeIndex( end, rangeStart - 1, styleCount ) ); newRanges = new int[ ( rangeEnd - rangeStart + 1 ) << 1 ]; for( int i = rangeStart, j = 0; i <= rangeEnd; i++, j += 2 ) { StyleRange style = styles[ i ]; newRanges[ j ] = style.start; newRanges[ j + 1 ] = style.length; } } if( start > newRanges[ 0 ] ) { newRanges[ 1 ] = newRanges[ 0 ] + newRanges[ 1 ] - start; newRanges[ 0 ] = start; } if( end < newRanges[ newRanges.length - 2 ] + newRanges[ newRanges.length - 1 ] - 1 ) { newRanges[ newRanges.length - 1 ] = end - newRanges[ newRanges.length - 2 ] + 1; } return newRanges; } StyleRange[] getStyleRanges( int start, int length, boolean includeRanges ) { StyleRange[] newStyles; int end = start + length - 1; if( ranges != null ) { int rangeCount = styleCount << 1; int rangeStart = getRangeIndex( start, -1, rangeCount ); if( rangeStart >= rangeCount ) return null; if( ranges[ rangeStart ] > end ) return null; int rangeEnd = Math.min( rangeCount - 2, getRangeIndex( end, rangeStart - 1, rangeCount ) + 1 ); newStyles = new StyleRange[ ( ( rangeEnd - rangeStart ) >> 1 ) + 1 ]; if( includeRanges ) { for( int i = rangeStart, j = 0; i <= rangeEnd; i += 2, j++ ) { newStyles[ j ] = ( StyleRange )styles[ i >> 1 ].clone(); newStyles[ j ].start = ranges[ i ]; newStyles[ j ].length = ranges[ i + 1 ]; } } else { System.arraycopy( styles, rangeStart >> 1, newStyles, 0, newStyles.length ); } } else { int rangeStart = getRangeIndex( start, -1, styleCount ); if( rangeStart >= styleCount ) return null; if( styles[ rangeStart ].start > end ) return null; int rangeEnd = Math.min( styleCount - 1, getRangeIndex( end, rangeStart - 1, styleCount ) ); newStyles = new StyleRange[ rangeEnd - rangeStart + 1 ]; System.arraycopy( styles, rangeStart, newStyles, 0, newStyles.length ); } StyleRange style = newStyles[ 0 ]; if( start > style.start ) { if( !includeRanges || ranges == null ) newStyles[ 0 ] = style = ( StyleRange )style.clone(); style.length = style.start + style.length - start; style.start = start; } style = newStyles[ newStyles.length - 1 ]; if( end < style.start + style.length - 1 ) { if( end < style.start ) { StyleRange[] tmp = new StyleRange[ newStyles.length - 1 ]; System.arraycopy( newStyles, 0, tmp, 0, newStyles.length - 1 ); newStyles = tmp; } else { if( !includeRanges || ranges == null ) newStyles[ newStyles.length - 1 ] = style = ( StyleRange )style.clone(); style.length = end - style.start + 1; } } return newStyles; } StyleRange getStyleRange( StyleRange style ) { if( style.start == 0 && style.length == 0 && style.fontStyle == SWT.NORMAL ) return style; StyleRange clone = ( StyleRange )style.clone(); clone.start = clone.length = 0; clone.fontStyle = SWT.NORMAL; return clone; } void setStyleRanges( int[] newRanges, StyleRange[] newStyles ) { if( newStyles == null ) { stylesSetCount = styleCount = 0; ranges = null; styles = null; stylesSet = null; return; } if( newRanges == null && COMPACT_STYLES ) { newRanges = new int[ newStyles.length << 1 ]; StyleRange[] tmpStyles = new StyleRange[ newStyles.length ]; if( stylesSet == null ) stylesSet = new StyleRange[ 4 ]; for( int i = 0, j = 0; i < newStyles.length; i++ ) { StyleRange newStyle = newStyles[ i ]; newRanges[ j++ ] = newStyle.start; newRanges[ j++ ] = newStyle.length; int index = 0; while( index < stylesSetCount ) { if( stylesSet[ index ].similarTo( newStyle ) ) break; index++; } if( index == stylesSetCount ) { if( stylesSetCount == stylesSet.length ) { StyleRange[] tmpStylesSet = new StyleRange[ stylesSetCount + 4 ]; System.arraycopy( stylesSet, 0, tmpStylesSet, 0, stylesSetCount ); stylesSet = tmpStylesSet; } stylesSet[ stylesSetCount++ ] = newStyle; } tmpStyles[ i ] = stylesSet[ index ]; } newStyles = tmpStyles; } if( styleCount == 0 ) { if( newRanges != null ) { ranges = new int[ newRanges.length ]; System.arraycopy( newRanges, 0, ranges, 0, ranges.length ); } styles = new StyleRange[ newStyles.length ]; System.arraycopy( newStyles, 0, styles, 0, styles.length ); styleCount = newStyles.length; return; } if( newRanges != null && ranges == null ) { ranges = new int[ styles.length << 1 ]; for( int i = 0, j = 0; i < styleCount; i++ ) { ranges[ j++ ] = styles[ i ].start; ranges[ j++ ] = styles[ i ].length; } } if( newRanges == null && ranges != null ) { newRanges = new int[ newStyles.length << 1 ]; for( int i = 0, j = 0; i < newStyles.length; i++ ) { newRanges[ j++ ] = newStyles[ i ].start; newRanges[ j++ ] = newStyles[ i ].length; } } if( ranges != null ) { int rangeCount = styleCount << 1; int start = newRanges[ 0 ]; int modifyStart = getRangeIndex( start, -1, rangeCount ), modifyEnd; boolean insert = modifyStart == rangeCount; if( !insert ) { int end = newRanges[ newRanges.length - 2 ] + newRanges[ newRanges.length - 1 ]; modifyEnd = getRangeIndex( end, modifyStart - 1, rangeCount ); insert = modifyStart == modifyEnd && ranges[ modifyStart ] >= end; } if( insert ) { addMerge( newRanges, newStyles, newRanges.length, modifyStart, modifyStart ); return; } modifyEnd = modifyStart; int[] mergeRanges = new int[ 6 ]; StyleRange[] mergeStyles = new StyleRange[ 3 ]; for( int i = 0; i < newRanges.length; i += 2 ) { int newStart = newRanges[ i ]; int newEnd = newStart + newRanges[ i + 1 ]; if( newStart == newEnd ) continue; int modifyLast = 0, mergeCount = 0; while( modifyEnd < rangeCount ) { if( newStart >= ranges[ modifyStart ] + ranges[ modifyStart + 1 ] ) modifyStart += 2; if( ranges[ modifyEnd ] + ranges[ modifyEnd + 1 ] > newEnd ) break; modifyEnd += 2; } if( ranges[ modifyStart ] < newStart && newStart < ranges[ modifyStart ] + ranges[ modifyStart + 1 ] ) { mergeStyles[ mergeCount >> 1 ] = styles[ modifyStart >> 1 ]; mergeRanges[ mergeCount ] = ranges[ modifyStart ]; mergeRanges[ mergeCount + 1 ] = newStart - ranges[ modifyStart ]; mergeCount += 2; } mergeStyles[ mergeCount >> 1 ] = newStyles[ i >> 1 ]; mergeRanges[ mergeCount ] = newStart; mergeRanges[ mergeCount + 1 ] = newRanges[ i + 1 ]; mergeCount += 2; if( modifyEnd < rangeCount && ranges[ modifyEnd ] < newEnd && newEnd < ranges[ modifyEnd ] + ranges[ modifyEnd + 1 ] ) { mergeStyles[ mergeCount >> 1 ] = styles[ modifyEnd >> 1 ]; mergeRanges[ mergeCount ] = newEnd; mergeRanges[ mergeCount + 1 ] = ranges[ modifyEnd ] + ranges[ modifyEnd + 1 ] - newEnd; mergeCount += 2; modifyLast = 2; } int grow = addMerge( mergeRanges, mergeStyles, mergeCount, modifyStart, modifyEnd + modifyLast ); rangeCount += grow; modifyStart = modifyEnd += grow; } } else { int start = newStyles[ 0 ].start; int modifyStart = getRangeIndex( start, -1, styleCount ), modifyEnd; boolean insert = modifyStart == styleCount; if( !insert ) { int end = newStyles[ newStyles.length - 1 ].start + newStyles[ newStyles.length - 1 ].length; modifyEnd = getRangeIndex( end, modifyStart - 1, styleCount ); insert = modifyStart == modifyEnd && styles[ modifyStart ].start >= end; } if( insert ) { addMerge( newStyles, newStyles.length, modifyStart, modifyStart ); return; } modifyEnd = modifyStart; StyleRange[] mergeStyles = new StyleRange[ 3 ]; for( int i = 0; i < newStyles.length; i++ ) { StyleRange newStyle = newStyles[ i ], style; int newStart = newStyle.start; int newEnd = newStart + newStyle.length; if( newStart == newEnd ) continue; int modifyLast = 0, mergeCount = 0; while( modifyEnd < styleCount ) { if( newStart >= styles[ modifyStart ].start + styles[ modifyStart ].length ) modifyStart++; if( styles[ modifyEnd ].start + styles[ modifyEnd ].length > newEnd ) break; modifyEnd++; } style = styles[ modifyStart ]; if( style.start < newStart && newStart < style.start + style.length ) { style = mergeStyles[ mergeCount++ ] = ( StyleRange )style.clone(); style.length = newStart - style.start; } mergeStyles[ mergeCount++ ] = newStyle; if( modifyEnd < styleCount ) { style = styles[ modifyEnd ]; if( style.start < newEnd && newEnd < style.start + style.length ) { style = mergeStyles[ mergeCount++ ] = ( StyleRange )style.clone(); style.length += style.start - newEnd; style.start = newEnd; modifyLast = 1; } } int grow = addMerge( mergeStyles, mergeCount, modifyStart, modifyEnd + modifyLast ); modifyStart = modifyEnd += grow; } } } void updateRanges( int start, int replaceCharCount, int newCharCount ) { if( styleCount == 0 || ( replaceCharCount == 0 && newCharCount == 0 ) ) return; if( ranges != null ) { int rangeCount = styleCount << 1; int modifyStart = getRangeIndex( start, -1, rangeCount ); if( modifyStart == rangeCount ) return; int end = start + replaceCharCount; int modifyEnd = getRangeIndex( end, modifyStart - 1, rangeCount ); int offset = newCharCount - replaceCharCount; if( modifyStart == modifyEnd && ranges[ modifyStart ] < start && end < ranges[ modifyEnd ] + ranges[ modifyEnd + 1 ] ) { if( newCharCount == 0 ) { ranges[ modifyStart + 1 ] -= replaceCharCount; modifyEnd += 2; } else { if( rangeCount + 2 > ranges.length ) { int[] newRanges = new int[ ranges.length + ( GROW << 1 ) ]; System.arraycopy( ranges, 0, newRanges, 0, rangeCount ); ranges = newRanges; StyleRange[] newStyles = new StyleRange[ styles.length + GROW ]; System.arraycopy( styles, 0, newStyles, 0, styleCount ); styles = newStyles; } System.arraycopy( ranges, modifyStart + 2, ranges, modifyStart + 4, rangeCount - ( modifyStart + 2 ) ); System.arraycopy( styles, ( modifyStart + 2 ) >> 1, styles, ( modifyStart + 4 ) >> 1, styleCount - ( ( modifyStart + 2 ) >> 1 ) ); ranges[ modifyStart + 3 ] = ranges[ modifyStart ] + ranges[ modifyStart + 1 ] - end; ranges[ modifyStart + 2 ] = start + newCharCount; ranges[ modifyStart + 1 ] = start - ranges[ modifyStart ]; styles[ ( modifyStart >> 1 ) + 1 ] = styles[ modifyStart >> 1 ]; rangeCount += 2; styleCount++; modifyEnd += 4; } if( offset != 0 ) { for( int i = modifyEnd; i < rangeCount; i += 2 ) { ranges[ i ] += offset; } } } else { if( ranges[ modifyStart ] < start && start < ranges[ modifyStart ] + ranges[ modifyStart + 1 ] ) { ranges[ modifyStart + 1 ] = start - ranges[ modifyStart ]; modifyStart += 2; } if( modifyEnd < rangeCount && ranges[ modifyEnd ] < end && end < ranges[ modifyEnd ] + ranges[ modifyEnd + 1 ] ) { ranges[ modifyEnd + 1 ] = ranges[ modifyEnd ] + ranges[ modifyEnd + 1 ] - end; ranges[ modifyEnd ] = end; } if( offset != 0 ) { for( int i = modifyEnd; i < rangeCount; i += 2 ) { ranges[ i ] += offset; } } System.arraycopy( ranges, modifyEnd, ranges, modifyStart, rangeCount - modifyEnd ); System.arraycopy( styles, modifyEnd >> 1, styles, modifyStart >> 1, styleCount - ( modifyEnd >> 1 ) ); styleCount -= ( modifyEnd - modifyStart ) >> 1; } } else { int modifyStart = getRangeIndex( start, -1, styleCount ); if( modifyStart == styleCount ) return; int end = start + replaceCharCount; int modifyEnd = getRangeIndex( end, modifyStart - 1, styleCount ); int offset = newCharCount - replaceCharCount; if( modifyStart == modifyEnd && styles[ modifyStart ].start < start && end < styles[ modifyEnd ].start + styles[ modifyEnd ].length ) { if( newCharCount == 0 ) { styles[ modifyStart ].length -= replaceCharCount; modifyEnd++; } else { if( styleCount + 1 > styles.length ) { StyleRange[] newStyles = new StyleRange[ styles.length + GROW ]; System.arraycopy( styles, 0, newStyles, 0, styleCount ); styles = newStyles; } System.arraycopy( styles, modifyStart + 1, styles, modifyStart + 2, styleCount - ( modifyStart + 1 ) ); styles[ modifyStart + 1 ] = ( StyleRange )styles[ modifyStart ].clone(); styles[ modifyStart + 1 ].length = styles[ modifyStart ].start + styles[ modifyStart ].length - end; styles[ modifyStart + 1 ].start = start + newCharCount; styles[ modifyStart ].length = start - styles[ modifyStart ].start; styleCount++; modifyEnd += 2; } if( offset != 0 ) { for( int i = modifyEnd; i < styleCount; i++ ) { styles[ i ].start += offset; } } } else { if( styles[ modifyStart ].start < start && start < styles[ modifyStart ].start + styles[ modifyStart ].length ) { styles[ modifyStart ].length = start - styles[ modifyStart ].start; modifyStart++; } if( modifyEnd < styleCount && styles[ modifyEnd ].start < end && end < styles[ modifyEnd ].start + styles[ modifyEnd ].length ) { styles[ modifyEnd ].length = styles[ modifyEnd ].start + styles[ modifyEnd ].length - end; styles[ modifyEnd ].start = end; } if( offset != 0 ) { for( int i = modifyEnd; i < styleCount; i++ ) { styles[ i ].start += offset; } } System.arraycopy( styles, modifyEnd, styles, modifyStart, styleCount - modifyEnd ); styleCount -= modifyEnd - modifyStart; } } } }