/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.ui.richtext; import static org.xmind.ui.richtext.IRichDocument.EMPTY_HYPERLINK; import static org.xmind.ui.richtext.IRichDocument.EMPTY_IMAGES; import static org.xmind.ui.richtext.IRichDocument.EMPTY_LINE_STYLES; import static org.xmind.ui.richtext.IRichDocument.EMPTY_TEXT_STYLES; import static org.xmind.ui.richtext.ImagePlaceHolder.PLACE_HOLDER; import java.util.ArrayList; import java.util.List; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextInputListener; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.IUndoManager; import org.eclipse.jface.text.TextPresentation; import org.eclipse.jface.text.TextViewer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.Bullet; import org.eclipse.swt.custom.PaintObjectEvent; import org.eclipse.swt.custom.PaintObjectListener; import org.eclipse.swt.custom.ST; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.GlyphMetrics; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Display; import org.xmind.ui.viewers.SWTUtils; /** * @author Frank Shaka */ public class RichTextRenderer implements IRichTextRenderer { private class DocumentListener implements IDocumentListener { public void documentAboutToBeChanged(DocumentEvent event) { lineRangeBeforeChange = getLineRange(event.getOffset(), event.getLength()); } public void documentChanged(DocumentEvent event) { if (ignoreDocumentChange) return; updateDocumentPositions(event); } } private class RichDocumentListener implements IRichDocumentListener { public void imageChanged(IRichDocument document, ImagePlaceHolder[] oldStyles, ImagePlaceHolder[] newStyles) { asyncRefreshViewer(); } public void lineStyleChanged(IRichDocument document, LineStyle[] oldStyles, LineStyle[] newStyles) { asyncRefreshViewer(); } public void textStyleChanged(IRichDocument document, StyleRange[] oldStyles, StyleRange[] newStyles) { asyncRefreshViewer(); } public void hyperlinkChanged(IRichDocument document, Hyperlink[] oldHyperlinks, Hyperlink[] newHyperlinks) { asyncRefreshViewer(); } } private TextViewer viewer; private IRichDocument document; private IDocumentListener documentChangeHandler; private IRichDocumentListener richDocumentChangeHandler; private StyleRange selectionTextStyle; private LineStyle selectionLineStyle; private boolean ignoreDocumentChange = false; private Point lineRangeBeforeChange = null; private boolean asyncRefreshing = false; public RichTextRenderer(TextViewer viewer) { this.viewer = viewer; initialize(viewer); } private void initialize(final TextViewer viewer) { this.documentChangeHandler = new DocumentListener(); this.richDocumentChangeHandler = new RichDocumentListener(); IDocument doc = viewer.getDocument(); if (doc instanceof IRichDocument) { this.document = (IRichDocument) doc; this.document.addDocumentListener(documentChangeHandler); this.document.addRichDocumentListener(richDocumentChangeHandler); } viewer.addTextInputListener(new ITextInputListener() { public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { if (oldInput != newInput) { if (document != null) { document.removeDocumentListener(documentChangeHandler); document.removeRichDocumentListener( richDocumentChangeHandler); } if (newInput != null && newInput instanceof IRichDocument) { document = (IRichDocument) newInput; document.addDocumentListener(documentChangeHandler); document.addRichDocumentListener( richDocumentChangeHandler); } else { document = null; } initialize(); } } public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { } }); viewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { ISelection sel = event.getSelection(); if (sel instanceof ITextSelection) { ITextSelection ts = (ITextSelection) sel; int offset = ts.getOffset(); int length = ts.getLength(); updateSelectionTextStyle(offset, length); if (document != null) { try { int line = document.getLineOfOffset(offset); updateSelectionLineStyle(line); } catch (BadLocationException e) { } } } } }); viewer.getTextWidget() .addPaintObjectListener(new PaintObjectListener() { public void paintObject(PaintObjectEvent event) { // if (document == null) // return; StyleRange style = event.style; int start = style.start; Image image = document.findImage(start); // Image image = (Image) style.data; if (image != null && !image.isDisposed()) { GC gc = event.gc; int x = event.x; GlyphMetrics metrics = style.metrics; if (metrics != null) { int y = event.y + event.ascent - metrics.ascent; gc.drawImage(image, x, y); } } } }); viewer.getTextWidget().addKeyListener(new KeyListener() { public void keyReleased(KeyEvent e) { } public void keyPressed(KeyEvent e) { if (SWTUtils.matchKey(e.stateMask, e.keyCode, 0, SWT.CR)) { if (document == null) return; handEntryKey(); } else if (SWTUtils.matchKey(e.stateMask, e.keyCode, 0, SWT.BS)) { if (document == null) return; handleBackspaceKey(); } } }); } private void handEntryKey() { if (LineStyle.NONE_STYLE.equals(selectionLineStyle.bulletStyle)) return; List<LineStyle> lineStyles = getModifiableLineStyles(); if (lineStyles == null || lineStyles.isEmpty()) return; Point point = getSelectedLineRange(); int startLine = point.x; if (find(startLine)) { LineStyle lineStyle = findLineStyleAt(startLine); lineStyles.remove(lineStyle); lineStyles.remove(findLineStyleAt(startLine - 1)); document.setLineStyles(lineStyles.toArray(EMPTY_LINE_STYLES)); } } private boolean find(int startLine) { try { IRegion r1 = document.getLineInformation(startLine - 1); String content1 = document.get(r1.getOffset(), r1.getLength()); if (!"".equals(content1)) //$NON-NLS-1$ return false; String style1 = findLineStyleAt(startLine - 1).bulletStyle; if (LineStyle.NONE_STYLE.equals(style1)) return false; IRegion r = document.getLineInformation(startLine); String content2 = document.get(r.getOffset(), r.getLength()); if (!"".equals(content2)) //$NON-NLS-1$ return false; String style2 = findLineStyleAt(startLine).bulletStyle; if (LineStyle.NONE_STYLE.equals(style2)) return false; return true; } catch (BadLocationException e) { } return false; } private void handleBackspaceKey() { try { IRegion lineInfo = document.getLineInformation(0); if (lineInfo.getLength() == 0 && lineInfo.getOffset() == 0) { getBulletModifier(LineStyle.NONE_STYLE).updateViewer(viewer, 0, 1); } } catch (BadLocationException e1) { } } private void initialize() { this.selectionTextStyle = (StyleRange) RichTextUtils.DEFAULT_STYLE .clone(); this.ignoreDocumentChange = false; refreshViewer(); } private void asyncRefreshViewer() { if (asyncRefreshing) return; asyncRefreshing = true; Display.getCurrent().asyncExec(new Runnable() { public void run() { refreshViewer(); asyncRefreshing = false; } }); } private void refreshViewer() { if (document != null && viewer != null) { refreshViewer(document.getTextStyles()); viewer.invalidateTextPresentation(); LineStyle[] lineStyles = document.getLineStyles(); int lineStyleIndex = lineStyles.length == 0 ? -1 : 0; LineStyle lineStyle = lineStyleIndex < 0 ? null : lineStyles[lineStyleIndex]; int lines = document.getNumberOfLines(); for (int i = 0; i < lines; i++) { if (lineStyle != null && i == lineStyle.lineIndex) { getAlignmentModifier(lineStyle.alignment) .updateViewer(viewer, lineStyle.lineIndex, 1); getBulletModifier(lineStyle.bulletStyle) .updateViewer(viewer, lineStyle.lineIndex, 1); lineStyleIndex++; lineStyle = lineStyleIndex >= lineStyles.length ? null : lineStyles[lineStyleIndex]; } else { getAlignmentModifier(SWT.LEFT).updateViewer(viewer, i, 1); getBulletModifier(LineStyle.NONE_STYLE).updateViewer(viewer, i, 1); } } } } public StyleRange getSelectionTextStyle() { updateSelectionTextStyle(); return selectionTextStyle; } private void updateSelectionTextStyle() { Point range = viewer.getSelectedRange(); updateSelectionTextStyle(range.x, range.y); } private void updateSelectionTextStyle(int offset, int length) { if (selectionTextStyle == null || offset != selectionTextStyle.start || length != selectionTextStyle.length) { StyleRange style = findTextStyleAt(offset, length); if (style == null) { style = RichTextUtils.DEFAULT_STYLE; } selectionTextStyle = (StyleRange) style.clone(); selectionTextStyle.metrics = null; } selectionTextStyle.start = offset; selectionTextStyle.length = length; } protected StyleRange findTextStyleAt(int offset, int length) { return document == null ? null : document.findTextStyle(offset, length); } protected LineStyle getSelectionLineStyle() { updateSelectionLineStyle(); return selectionLineStyle; } private void updateSelectionLineStyle() { Point range = getSelectedLineRange(); updateSelectionLineStyle(range.x); } private void updateSelectionLineStyle(int startLine) { if (selectionLineStyle == null || selectionLineStyle.lineIndex != startLine) { LineStyle lineStyle = findLineStyleAt(startLine); if (lineStyle == null) { lineStyle = RichTextUtils.DEFAULT_LINE_STYLE; } selectionLineStyle = (LineStyle) lineStyle.clone(); } selectionLineStyle.lineIndex = startLine; LineStyle style = findLineStyleAt(startLine); if (style != null) selectionLineStyle.bulletStyle = style.bulletStyle; } private LineStyle findLineStyleAt(int startLine) { return document == null ? null : document.findLineStyle(startLine); } public Color getSelectionBackground() { return RichTextUtils.getBackground(getSelectionTextStyle()); } public Font getSelectionFont() { return RichTextUtils.getFont(getSelectionTextStyle()); } public boolean getSelectionFontBold() { return RichTextUtils.isBold(getSelectionTextStyle()); } public String getSelectionFontFace() { return RichTextUtils.getFontFace(getSelectionTextStyle()); } public boolean getSelectionFontItalic() { return RichTextUtils.isItalic(getSelectionTextStyle()); } public int getSelectionFontSize() { return RichTextUtils.getFontSize(getSelectionTextStyle()); } public boolean getSelectionFontStrikeout() { return getSelectionTextStyle().strikeout; } public boolean getSelectionFontUnderline() { return getSelectionTextStyle().underline; } public Color getSelectionForeground() { return RichTextUtils.getForeground(getSelectionTextStyle()); } public int getSelectionParagraphAlignment() { return getSelectionLineStyle().alignment; } public int getSelectionParagraphIndent() { return getSelectionLineStyle().indent; } public boolean getBulletSelectionParagraph() { LineStyle lineStyle = getSelectionLineStyle(); return LineStyle.BULLET.equals(lineStyle.bulletStyle); } public boolean getNumberSelectionParagraph() { LineStyle lineStyle = getSelectionLineStyle(); return LineStyle.NUMBER.equals(lineStyle.bulletStyle); } public void indentSelectionParagraph() { modifySelectionLineStyles(getIndentModifier(1)); } public void bulletSelectionParagraph(boolean bullet) { String bulletStyle = bullet ? LineStyle.BULLET : LineStyle.NONE_STYLE; modifySelectionLineStyles(getBulletModifier(bulletStyle)); } public void numberSelectionParagraph(boolean number) { String bulletStyle = number ? LineStyle.NUMBER : LineStyle.NONE_STYLE; modifySelectionLineStyles(getBulletModifier(bulletStyle)); } public void insertHyperlink(String href) { insertHyperlink(href, null); } public void insertHyperlink(String href, String displayText) { Point range = viewer.getSelectedRange(); updateSelectionTextStyle(); commitLastChange(); beginCompoundChange(); range = modifyHyperlinksPosition(range); int start = range.x; int oldLength = range.y; int newLength = oldLength; if (displayText != null) { newLength = displayText.length(); try { document.replace(start, oldLength, displayText); } catch (BadLocationException e) { e.printStackTrace(); return; } } addHyperlinkToDocument(start, newLength, href); setSelectedRange(start + newLength, 0); endCompoundChange(); commitLastChange(); } private Point modifyHyperlinksPosition(Point range) { int start = range.x; int oldLength = range.y; int oldEnd = start + oldLength; int length = document.getLength(); if (start == length) return range; List<Hyperlink> hyperlinks = getModifiableHyperlinks(); for (int i = 0; i < hyperlinks.size(); i++) { Hyperlink hyper = hyperlinks.get(i); int hyperStart = hyper.start; int hyperEnd = hyper.end(); if (oldEnd <= hyperStart) break; if (start >= hyperStart && oldEnd <= hyperEnd) { start = hyperStart; oldLength = hyperEnd - hyperStart; break; } } return new Point(start, oldLength); } private void addHyperlinkToDocument(int start, int newLength, String hyperlink) { int end = start + newLength; List<Hyperlink> oldHyperlinks = getModifiableHyperlinks(); for (int i = 0; i < oldHyperlinks.size(); i++) { Hyperlink indexHyper = oldHyperlinks.get(i); if (end <= indexHyper.start) break; int hyperStart = indexHyper.start; int hyperEnd = indexHyper.end(); if (hyperStart < end && start < hyperEnd) { if (end <= hyperEnd && start < hyperStart) { indexHyper.length = hyperEnd - end; indexHyper.start = end; } if (start > hyperStart && end > hyperEnd) { indexHyper.length = end - hyperStart; } if (end <= hyperEnd && start >= hyperStart) { oldHyperlinks.remove(i); break; } } } int index = getInsertHyperlinkIndex(start, oldHyperlinks); Hyperlink hyper = new Hyperlink(start, newLength, hyperlink); oldHyperlinks.add(index, hyper); document.setHyperlinks(oldHyperlinks.toArray(EMPTY_HYPERLINK)); } private List<Hyperlink> getModifiableHyperlinks() { Hyperlink[] oldHyperlinks = document.getHyperlinks(); List<Hyperlink> newHyperlinks = new ArrayList<Hyperlink>( oldHyperlinks.length); for (Hyperlink hyper : oldHyperlinks) newHyperlinks.add((Hyperlink) hyper.clone()); return newHyperlinks; } private int getInsertHyperlinkIndex(int offset, List<Hyperlink> hyperlinks) { int i; for (i = 0; i < hyperlinks.size(); i++) { Hyperlink hyperlink = hyperlinks.get(i); if (hyperlink.start >= offset) return i; } return i; } public Hyperlink[] getSelectionHyperlinks() { Point range = viewer.getSelectedRange(); Hyperlink[] hyperlinks = document.getHyperlinks(); List<Hyperlink> list = RichTextUtils.getHyperlinksInRange(hyperlinks, range.x, range.x + range.y); return list.toArray(new Hyperlink[list.size()]); } public void outdentSelectionParagraph() { modifySelectionLineStyles(getIndentModifier(-1)); } public void insertImage(Image image) { Point range = viewer.getSelectedRange(); int start = range.x; int oldLength = range.y; String placeHolder = PLACE_HOLDER; int newLength = placeHolder.length(); updateSelectionTextStyle(); commitLastChange(); beginCompoundChange(); try { document.replace(start, oldLength, placeHolder); } catch (BadLocationException e) { e.printStackTrace(); return; } addImageToDocument(image, start); addImageStyleRangeToDocument(image, start, newLength); setSelectedRange(start + newLength, 0); endCompoundChange(); commitLastChange(); } public void setSelectionBackground(Color color) { modifySelectionTextStyles(getBackgroundModifier(color)); } public void setSelectionFont(Font font) { modifySelectionTextStyles(getFontModifier(font)); } public void setSelectionFontBold(final boolean bold) { modifySelectionTextStyles(getBoldModifier(bold)); } public void setSelectionFontFace(String fontFace) { modifySelectionTextStyles(getFontFaceModifier(fontFace)); } public void setSelectionFontItalic(final boolean italic) { modifySelectionTextStyles(getItalicModifier(italic)); } public void setSelectionFontSize(int size) { modifySelectionTextStyles(getFontSizeModifier(size)); } public void setSelectionFontStrikeout(boolean strikeout) { modifySelectionTextStyles(getStrikeoutModifier(strikeout)); } public void setSelectionFontUnderline(boolean underline) { modifySelectionTextStyles(getUnderlineModifier(underline)); } public void setSelectionForeground(Color color) { modifySelectionTextStyles(getForegroundModifier(color)); } public void setSelectionParagraphAlignment(int alignment) { modifySelectionLineStyles(getAlignmentModifier(alignment)); } public void setSelectionParagraphIndent(int insertIndent) { modifySelectionLineStyles(getIndentReplacer(insertIndent)); } protected void updateDocumentPositions(DocumentEvent event) { if (document == null) return; int start = event.getOffset();//the position of Cursor int oldLength = event.getLength();//the selection text's length int newLength = event.getText().length();//later input the text's length updateTextStylesInDocument(start, oldLength, newLength); updateLineStylesInDocument(start, oldLength, newLength, event.getText()); updateImagesInDocument(start, oldLength, newLength); updateHyperlinksInDocument(start, oldLength, newLength); } private void updateHyperlinksInDocument(int start, int oldLength, int newLength) { List<Hyperlink> hyperlinks = getModifiableHyperlinks(); RichTextUtils.updateHyperlinksPositions(start, oldLength, newLength, hyperlinks); Hyperlink[] modifiedHyperlinks = hyperlinks.toArray(EMPTY_HYPERLINK); document.setHyperlinks(modifiedHyperlinks); } private void updateTextStylesInDocument(int start, int oldLength, int newLength) { List<StyleRange> styles = getModifiableTextStyles(); updateSelectionTextStyle(start, oldLength); RichTextUtils.replaceStyleRanges(start, oldLength, newLength, styles, selectionTextStyle); StyleRange[] modifiedStyleRanges = styles.toArray(EMPTY_TEXT_STYLES); document.setTextStyles(modifiedStyleRanges); selectionTextStyle.start = (start + newLength); selectionTextStyle.length = 0; } private void updateLineStylesInDocument(int start, int oldLength, int newLength, String text) { if (lineRangeBeforeChange == null || lineRangeBeforeChange.x < 0 || lineRangeBeforeChange.y < 0) return; Point currentLineRange = getLineRange(start, newLength); int startLine = currentLineRange.x; int lineCount = currentLineRange.y; if (startLine < 0 || lineCount < 0) return; List<LineStyle> lineStyles = getModifiableLineStyles(); RichTextUtils.updateLineStylePositions(startLine, lineRangeBeforeChange.y, currentLineRange.y, lineStyles, document); LineStyle[] modifiedLineStyles = lineStyles.toArray(EMPTY_LINE_STYLES); document.setLineStyles(modifiedLineStyles); lineRangeBeforeChange = null; } private void updateImagesInDocument(int start, int oldLength, int newLength) { List<ImagePlaceHolder> images = getModifiableImages(); RichTextUtils.updateImagePositions(start, oldLength, newLength, images); ImagePlaceHolder[] modifiedImages = images.toArray(EMPTY_IMAGES); document.setImages(modifiedImages); } private List<StyleRange> getModifiableTextStyles() { StyleRange[] oldTextStyles = document.getTextStyles(); List<StyleRange> newStyles = new ArrayList<StyleRange>( oldTextStyles.length); for (StyleRange textStyle : oldTextStyles) { newStyles.add((StyleRange) textStyle.clone()); } return newStyles; } private void addImageStyleRangeToDocument(Image image, int start, int length) { List<StyleRange> styles = getModifiableTextStyles(); StyleRange imageStyle = createImageStyle(start, length, image); RichTextUtils.replaceStyleRanges(start, length, length, styles, imageStyle); StyleRange[] modifiedStyleRanges = styles.toArray(EMPTY_TEXT_STYLES); document.setTextStyles(modifiedStyleRanges); } private void refreshViewer(StyleRange[] modifiedStyleRanges) { TextPresentation presentation = createPresentation(0, document.getLength(), modifiedStyleRanges); viewer.changeTextPresentation(presentation, true); } private void addImageToDocument(Image image, int start) { List<ImagePlaceHolder> oldImages = getModifiableImages(); int index = getInsertImageIndex(start, oldImages); ImagePlaceHolder imagePlaceHolder = new ImagePlaceHolder(start, image); oldImages.add(index, imagePlaceHolder); document.setImages(oldImages.toArray(EMPTY_IMAGES)); } private List<ImagePlaceHolder> getModifiableImages() { ImagePlaceHolder[] oldImages = document.getImages(); ArrayList<ImagePlaceHolder> newImages = new ArrayList<ImagePlaceHolder>( oldImages.length); for (ImagePlaceHolder img : oldImages) { newImages.add((ImagePlaceHolder) img.clone()); } return newImages; } private List<LineStyle> getModifiableLineStyles() { LineStyle[] oldLineStyles = document.getLineStyles(); ArrayList<LineStyle> newLineStyles = new ArrayList<LineStyle>( oldLineStyles.length); for (LineStyle line : oldLineStyles) { newLineStyles.add((LineStyle) line.clone()); } return newLineStyles; } private int getInsertImageIndex(int offset, List<ImagePlaceHolder> images) { int i; for (i = 0; i < images.size(); i++) { ImagePlaceHolder image = images.get(i); if (image.offset >= offset) return i; } return i; } private void setSelectedRange(int start, int length) { viewer.setSelectedRange(start, length); selectionTextStyle.start = start; selectionTextStyle.length = length; } private StyleRange createImageStyle(int start, int length, Image image) { StyleRange style = (StyleRange) selectionTextStyle.clone(); // style.data = image; style.start = start; style.length = length; Rectangle rect = image.getBounds(); style.metrics = new GlyphMetrics(rect.height, 0, rect.width); return style; } private void modifySelectionTextStyles(IStyleRangeModifier modifier) { if (document == null || modifier == null) return; commitLastChange(); beginCompoundChange(); modifier.modify(getSelectionTextStyle()); Point range = viewer.getSelectedRange(); int start = range.x; int length = range.y; modifyTextStyles(start, length, modifier); endCompoundChange(); commitLastChange(); } protected void beginCompoundChange() { if (viewer != null) { IUndoManager undoManager = viewer.getUndoManager(); if (undoManager != null) { undoManager.beginCompoundChange(); } } } protected void endCompoundChange() { if (viewer != null) { IUndoManager undoManager = viewer.getUndoManager(); if (undoManager != null) { undoManager.endCompoundChange(); } } } protected void commitLastChange() { if (viewer != null) { IUndoManager undoManager = viewer.getUndoManager(); if (undoManager != null && undoManager instanceof RichTextViewerUndoManager) { ((RichTextViewerUndoManager) undoManager).commit(); } } } private void modifyTextStyles(int start, int length, IStyleRangeModifier modifier) { if (document == null || modifier == null || length <= 0) return; List<StyleRange> styles = getModifiableTextStyles(); boolean changed = RichTextUtils.modifyTextStyles(start, length, styles, modifier); if (changed) { StyleRange[] modifiedStyleRanges = styles .toArray(EMPTY_TEXT_STYLES); document.setTextStyles(modifiedStyleRanges); } } private static TextPresentation createPresentation(int start, int length, StyleRange[] styleRanges) { TextPresentation presentation = new TextPresentation(); StyleRange defaultStyle = createDefaultStyle(start, length); presentation.setDefaultStyleRange(defaultStyle); presentation.replaceStyleRanges(styleRanges); return presentation; } private static StyleRange createDefaultStyle(int start, int length) { StyleRange defaultStyle = (StyleRange) RichTextUtils.DEFAULT_STYLE .clone(); defaultStyle.start = start; defaultStyle.length = length; return defaultStyle; } private void modifySelectionLineStyles(ILineStyleModifier modifier) { if (document == null || modifier == null) return; Point p = getSelectedLineRange(); if (p.x < 0 || p.y < 0) return; commitLastChange(); beginCompoundChange(); int startLine = p.x; int lineCount = p.y; modifyLineStyles(startLine, lineCount, modifier); endCompoundChange(); commitLastChange(); } private Point getSelectedLineRange() { Point p = viewer.getSelectedRange(); int offset = p.x; int length = p.y; return getLineRange(offset, length); } private Point getLineRange(int offset, int length) { if (document != null) { try { int startLine = document.getLineOfOffset(offset); int lineCount = document.getNumberOfLines(offset, length); return new Point(startLine, lineCount); } catch (BadLocationException e) { e.printStackTrace(); } } return new Point(-1, -1); } private void modifyLineStyles(int startLine, int lineCount, ILineStyleModifier modifier) { if (document == null || modifier == null || lineCount <= 0) return; List<LineStyle> lineStyles = getModifiableLineStyles(); boolean changed = RichTextUtils.modifyLineStyles(startLine, lineCount, lineStyles, modifier); if (changed) { LineStyle[] modifiedLineStyles = lineStyles .toArray(EMPTY_LINE_STYLES); modifier.updateViewer(viewer, startLine, lineCount); document.setLineStyles(modifiedLineStyles); } } private static StyleRangeModifier boldModifier = new StyleRangeModifier() { protected boolean modify(StyleRange style, Object value) { return RichTextUtils.setBold(style, (Boolean) value); } }; private static IStyleRangeModifier getBoldModifier(boolean bold) { boldModifier.setValue(bold); return boldModifier; } private static StyleRangeModifier italicModifier = new StyleRangeModifier() { protected boolean modify(StyleRange style, Object value) { return RichTextUtils.setItalic(style, (Boolean) value); } }; private static IStyleRangeModifier getItalicModifier(boolean italic) { italicModifier.setValue(italic); return italicModifier; } private static StyleRangeModifier underlineModifier = new StyleRangeModifier() { protected boolean modify(StyleRange style, Object value) { boolean v = (Boolean) value; if (style.underline == v) return false; style.underline = v; return true; } }; private static IStyleRangeModifier getUnderlineModifier(boolean italic) { underlineModifier.setValue(italic); return underlineModifier; } private static StyleRangeModifier strikeoutModifier = new StyleRangeModifier() { protected boolean modify(StyleRange style, Object value) { boolean v = (Boolean) value; if (style.strikeout == v) return false; style.strikeout = v; return true; } }; private static IStyleRangeModifier getStrikeoutModifier(boolean italic) { strikeoutModifier.setValue(italic); return strikeoutModifier; } private static StyleRangeModifier faceModifier = new StyleRangeModifier() { protected boolean modify(StyleRange style, Object value) { return RichTextUtils.setFontFace(style, (String) value); } }; private static IStyleRangeModifier getFontFaceModifier(String face) { faceModifier.setValue(face); return faceModifier; } private static StyleRangeModifier sizeModifier = new StyleRangeModifier() { protected boolean modify(StyleRange style, Object value) { return RichTextUtils.setFontSize(style, (Integer) value); } }; private static IStyleRangeModifier getFontSizeModifier(int size) { sizeModifier.setValue(size); return sizeModifier; } private static StyleRangeModifier fontModifier = new StyleRangeModifier() { protected boolean modify(StyleRange style, Object value) { return RichTextUtils.setFont(style, (Font) value); } }; private static IStyleRangeModifier getFontModifier(Font value) { fontModifier.setValue(value); return fontModifier; } private static StyleRangeModifier foregroundModifier = new StyleRangeModifier() { protected boolean modify(StyleRange style, Object value) { return RichTextUtils.setForeground(style, (Color) value); } }; private static IStyleRangeModifier getForegroundModifier(Color color) { foregroundModifier.setValue(color); return foregroundModifier; } private static StyleRangeModifier backgroundModifer = new StyleRangeModifier() { protected boolean modify(StyleRange style, Object value) { return RichTextUtils.setBackground(style, (Color) value); } }; private static IStyleRangeModifier getBackgroundModifier(Color color) { backgroundModifer.setValue(color); return backgroundModifer; } private static LineStyleModifier alignmentModifier = new LineStyleModifier() { protected boolean modify(LineStyle style, Object value) { int alignment = (Integer) value; if (style.alignment == alignment) return false; style.alignment = alignment; return true; } protected void updateViewer(TextViewer viewer, int startLine, int lineCount, Object value) { int alignment = (Integer) value; int endLine = startLine + lineCount; StyledText textWidget = viewer.getTextWidget(); for (int line = startLine; line < endLine; line++) { int wLine = viewer.modelLine2WidgetLine(line); textWidget.setLineAlignment(wLine, 1, alignment); } } }; private static ILineStyleModifier getAlignmentModifier(int alignment) { alignmentModifier.setValue(alignment); return alignmentModifier; } private static LineStyleModifier indentReplacer = new LineStyleModifier() { protected boolean modify(LineStyle style, Object value) { int indent = (Integer) value; if (indent == style.indent) return false; style.indent = indent; return true; } protected void updateViewer(TextViewer viewer, int startLine, int lineCount, Object value) { IDocument document = viewer.getDocument(); if (document == null) return; int indent = (Integer) value; int endLine = startLine + lineCount; //restore old selection. Point range = viewer.getSelectedRange(); range = new Point(range.x, range.y); try { for (int line = startLine; line < endLine; line++) { int realDeltaIndent = RichTextUtils .replaceDocumentIndent(document, line, indent); if (line == startLine) { range.x += realDeltaIndent; } else { range.y += realDeltaIndent; } } } catch (BadLocationException e) { e.printStackTrace(); } viewer.setSelectedRange(range.x, range.y); } }; private static ILineStyleModifier getIndentReplacer(int indent) { indentReplacer.setValue(indent); return indentReplacer; } private static LineStyleModifier indentModifier = new LineStyleModifier() { protected boolean modify(LineStyle style, Object value) { int deltaIndent = (Integer) value; if (deltaIndent == 0 || (deltaIndent < 0 && style.indent == 0)) return false; style.indent += deltaIndent; return true; } protected void updateViewer(TextViewer viewer, int startLine, int lineCount, Object value) { IDocument document = viewer.getDocument(); if (document == null) return; int deltaIndent = (Integer) value; int endLine = startLine + lineCount; //restore old selection. Point range = viewer.getSelectedRange(); range = new Point(range.x, range.y); try { for (int line = startLine; line < endLine; line++) { int realDeltaIndent = RichTextUtils.modifyDocumentIndent( viewer, document, line, deltaIndent); if (line == startLine) { range.x += realDeltaIndent; } else { range.y += realDeltaIndent; } } } catch (BadLocationException e) { e.printStackTrace(); } viewer.setSelectedRange(range.x, range.y); } }; private static ILineStyleModifier getIndentModifier(int deltaIndent) { indentModifier.setValue(deltaIndent); return indentModifier; } private static LineStyleModifier bulletModifier = new LineStyleModifier() { protected boolean modify(LineStyle style, Object value) { String v = (String) value; if (style.bulletStyle.equals(v)) return false; style.bulletStyle = v; return true; } protected void updateViewer(TextViewer viewer, int startLine, int lineCount, Object value) { String v = (String) value; StyledText styledText = viewer.getTextWidget(); StyleRange style = new StyleRange(); style.metrics = new GlyphMetrics(0, 0, 50); styledText.setLineBullet(startLine, lineCount, null); if (v.equals(LineStyle.BULLET)) { Bullet bullet = new Bullet(style); styledText.setLineBullet(startLine, lineCount, bullet); } else if (v.equals(LineStyle.NUMBER)) { // Bullet bullet = styledText.getLineBullet(startLine); // if (bullet.type != ST.BULLET_CUSTOM) Bullet bullet = new Bullet(ST.BULLET_CUSTOM, style); styledText.setLineBullet(startLine, lineCount, bullet); } else styledText.setLineBullet(startLine, lineCount, null); } }; private static ILineStyleModifier getBulletModifier(String bullet) { bulletModifier.setValue(bullet); return bulletModifier; } }