/* ****************************************************************************** * 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 java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.jface.text.Document; import org.eclipse.jface.util.SafeRunnable; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.graphics.Image; /** * @author Frank Shaka */ public class RichDocument extends Document implements IRichDocument { private StyleRange[] textStyles = EMPTY_TEXT_STYLES; private ImagePlaceHolder[] images = EMPTY_IMAGES; private LineStyle[] lineStyles = EMPTY_LINE_STYLES; private Hyperlink[] hyperlinks = EMPTY_HYPERLINK; private List<IRichDocumentListener> listeners = null; public RichDocument() { } public RichDocument(String initialContent) { super(initialContent); } public void addRichDocumentListener(IRichDocumentListener listener) { if (listeners == null) listeners = new ArrayList<IRichDocumentListener>(); listeners.add(listener); } public void removeRichDocumentListener(IRichDocumentListener listener) { if (listeners == null) return; listeners.remove(listener); if (listeners.isEmpty()) listeners = null; } protected void fireTextStylesChanged(final StyleRange[] oldTextStyles, final StyleRange[] newTextStyles) { if (listeners == null) return; for (final Object listener : listeners.toArray()) { SafeRunner.run(new SafeRunnable() { public void run() throws Exception { ((IRichDocumentListener) listener).textStyleChanged( RichDocument.this, oldTextStyles, newTextStyles); } }); } } protected void fireImagesChanged(final ImagePlaceHolder[] oldImages, final ImagePlaceHolder[] newImages) { if (listeners == null) return; for (final Object listener : listeners.toArray()) { SafeRunner.run(new SafeRunnable() { public void run() throws Exception { ((IRichDocumentListener) listener).imageChanged( RichDocument.this, oldImages, newImages); } }); } } protected void fireLineStylesChanged(final LineStyle[] oldLineStyles, final LineStyle[] newLineStyles) { if (listeners == null) return; for (final Object listener : listeners.toArray()) { SafeRunner.run(new SafeRunnable() { public void run() throws Exception { ((IRichDocumentListener) listener).lineStyleChanged( RichDocument.this, oldLineStyles, newLineStyles); } }); } } protected void fireHyperlinkChanged(final Hyperlink[] oldHyperlinks, final Hyperlink[] newHyperlinks) { if (listeners == null) return; for (final Object listener : listeners.toArray()) { SafeRunner.run(new SafeRunnable() { public void run() throws Exception { ((IRichDocumentListener) listener).hyperlinkChanged( RichDocument.this, oldHyperlinks, newHyperlinks); } }); } } public StyleRange findTextStyle(int offset, int length) { int end = length == 0 ? 0 : 1; for (StyleRange sr : textStyles) { if (sr.start <= offset && offset <= sr.start + sr.length - end) return sr; } return null; } public Image findImage(int offset) { for (ImagePlaceHolder imagePlaceHolder : images) { if (imagePlaceHolder.offset == offset) return imagePlaceHolder.image; } return null; } public LineStyle findLineStyle(int startLine) { for (LineStyle style : lineStyles) { if (style.lineIndex == startLine) return style; } return null; } public Hyperlink findHyperlink(int offset) { for (Hyperlink hyper : hyperlinks) { int start = hyper.start; int end = hyper.end(); if (start <= offset && offset <= end) return hyper; } return null; } public ImagePlaceHolder[] getImages() { return images; } public LineStyle[] getLineStyles() { return lineStyles; } public StyleRange[] getTextStyles() { return textStyles; } public Hyperlink[] getHyperlinks() { return hyperlinks; } public void setImages(ImagePlaceHolder[] images) { if (images == null) images = EMPTY_IMAGES; ImagePlaceHolder[] oldImages = this.images; ImagePlaceHolder[] newImages = images; if (!equals(oldImages, newImages)) { this.images = images; fireImagesChanged(oldImages, newImages); } } public void setLineStyles(LineStyle[] lines) { if (lines == null) lines = EMPTY_LINE_STYLES; LineStyle[] oldLineStyles = this.lineStyles; LineStyle[] newLineStyles = lines; if (!equals(oldLineStyles, newLineStyles)) { this.lineStyles = lines; fireLineStylesChanged(oldLineStyles, newLineStyles); } } public void setTextStyles(StyleRange[] styles) { if (styles == null) styles = EMPTY_TEXT_STYLES; StyleRange[] oldTextStyles = this.textStyles; StyleRange[] newTextStyles = styles; if (!equals(oldTextStyles, newTextStyles)) { this.textStyles = styles; fireTextStylesChanged(oldTextStyles, newTextStyles); } } public void setHyperlinks(Hyperlink[] hyperlinks) { if (hyperlinks == null) hyperlinks = EMPTY_HYPERLINK; Hyperlink[] oldHyperlinks = this.hyperlinks; Hyperlink[] newhyperlinks = hyperlinks; if (!equals(oldHyperlinks, newhyperlinks)) { this.hyperlinks = hyperlinks; fireHyperlinkChanged(oldHyperlinks, newhyperlinks); } } private static boolean equals(Object[] a1, Object[] a2) { return Arrays.equals(a1, a2); } }