org.xmind.ui.dialogs.StyledLink.java Source code

Java tutorial

Introduction

Here is the source code for org.xmind.ui.dialogs.StyledLink.java

Source

/* ******************************************************************************
 * 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.dialogs;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.TextLayout;
import org.eclipse.swt.graphics.TextStyle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.events.IHyperlinkListener;
import org.xmind.ui.resources.FontUtils;
import org.xmind.ui.util.EncodingUtils;

public class StyledLink extends Canvas {

    private static final int LINE_SPACING = 1;
    private static final int PARAGRAPH_SPACING = 7;

    private static class StyledParagraph {
        TextLayout text;
        int top;
    }

    private String text = ""; //$NON-NLS-1$

    private boolean styled;

    private StyledParagraph[] paragraphs = null;

    private int cachedWidth = -621735;

    private Point cachedSize = null;

    private boolean hovered = false;

    private Font boldFont = null;

    private Font italicFont = null;

    private Font boldItalicFont = null;

    private ListenerList listeners = new ListenerList();

    public StyledLink(Composite parent, int style) {
        super(parent, style | SWT.DOUBLE_BUFFERED);
        this.styled = (style & SWT.SIMPLE) == 0;
        super.setFont(FontUtils.getFont(JFaceResources.DEFAULT_FONT));
        super.setCursor(parent.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
        addPaintListener(new PaintListener() {
            public void paintControl(PaintEvent e) {
                drawParagraphs(e.gc);
            }
        });
        addMouseListener(new MouseListener() {

            boolean pressed = false;

            public void mouseUp(MouseEvent e) {
                Rectangle r = getBounds();
                if (pressed && e.x >= 0 && e.x < r.width && e.y >= 0 && e.y < r.height) {
                    fireLinkActivated(createLinkEvent(e));
                }
                pressed = false;
            }

            public void mouseDown(MouseEvent e) {
                pressed = true;
            }

            public void mouseDoubleClick(MouseEvent e) {
                pressed = false;
            }
        });
        addMouseTrackListener(new MouseTrackListener() {

            public void mouseHover(MouseEvent e) {
                setHovered(true);
            }

            public void mouseExit(MouseEvent e) {
                setHovered(false);
            }

            public void mouseEnter(MouseEvent e) {
                setHovered(true);
            }
        });
    }

    private void fireLinkActivated(HyperlinkEvent event) {
        for (Object listener : listeners.getListeners()) {
            ((IHyperlinkListener) listener).linkActivated(event);
        }
    }

    private HyperlinkEvent createLinkEvent(MouseEvent e) {
        HyperlinkEvent event = new HyperlinkEvent(this, null, getText(), e.stateMask);
        event.display = e.display;
        event.time = e.time;
        return event;
    }

    public void addHyperlinkListener(IHyperlinkListener listener) {
        listeners.add(listener);
    }

    public void removeHyperlinkListener(IHyperlinkListener listener) {
        listeners.remove(listener);
    }

    @Override
    public void setFont(Font font) {
        checkWidget();
        releaseLines();
        super.setFont(font);
    }

    @Override
    public void setBackground(Color color) {
        checkWidget();
        releaseLines();
        super.setBackground(color);
    }

    @Override
    public void setForeground(Color color) {
        checkWidget();
        releaseLines();
        super.setForeground(color);
    }

    @Override
    public void setEnabled(boolean enabled) {
        checkWidget();
        releaseLines();
        super.setEnabled(enabled);
    }

    private void releaseLines() {
        StyledParagraph[] ls = paragraphs;
        paragraphs = null;
        if (ls != null) {
            for (int i = 0; i < ls.length; i++) {
                ls[i].text.dispose();
            }
        }
    }

    private void ensureParagraphs(int wHint) {
        if (paragraphs != null)
            return;

        if (styled) {
            paragraphs = parseStyledText(text, wHint);
        } else {
            paragraphs = parseSimpleText(text, wHint);
        }
        cachedSize = new Point(0, 0);
        if (paragraphs.length == 0) {
            if (wHint >= 0)
                cachedSize.x = wHint;
        } else {
            for (StyledParagraph p : paragraphs) {
                Rectangle pBounds = p.text.getBounds();
                cachedSize.x = Math.max(cachedSize.x, pBounds.width);
                cachedSize.y = p.top + pBounds.height;
            }
        }
    }

    private StyledParagraph[] parseStyledText(String text, int wHint) {
        List<StyledParagraph> paragraphs = new ArrayList<StyledParagraph>(text.length() / 2);
        int contentStart = text.indexOf("<form>"); //$NON-NLS-1$
        if (contentStart >= 0) {
            contentStart += 6;
            int contentEnd = text.indexOf("</form>", contentStart); //$NON-NLS-1$
            if (contentEnd < 0)
                contentEnd = text.length();
            String content = text.substring(contentStart, contentEnd);

            int pStart = 0, pEnd = 0;
            int top = 0;
            String pText;
            StringBuilder pBuffer;
            List<StyleRange> styles;
            boolean bold = false, italic = false;
            while (pStart < content.length()) {
                if ("<p>".equals(content.substring(pStart, pStart + 3))) { //$NON-NLS-1$
                    pStart += 3;
                }
                pEnd = content.indexOf("</p>", pStart); //$NON-NLS-1$
                if (pEnd >= 0) {
                    pText = content.substring(pStart, pEnd);
                    pEnd += 4;
                } else {
                    pEnd = content.indexOf("<p>", pStart); //$NON-NLS-1$
                    if (pEnd >= 0) {
                        pText = content.substring(pStart, pEnd);
                        pEnd += 3;
                    } else {
                        pEnd = content.length();
                        pText = content.substring(pStart, pEnd);
                    }
                }
                pStart = pEnd;

                pBuffer = new StringBuilder(pText.length());
                styles = new ArrayList<StyleRange>(pText.length());

                int start = 0;
                int end = 0;
                while (start < pText.length()) {
                    if ("<b>".equals(pText.substring(start, start + 3))) { //$NON-NLS-1$
                        bold = true;
                        start += 3;
                    } else if ("<i>".equals(pText.substring(start, start + 3))) { //$NON-NLS-1$
                        italic = true;
                        start += 3;
                    } else if ("</b>".equals(pText.substring(start, start + 4))) { //$NON-NLS-1$
                        bold = false;
                        start += 4;
                    } else if ("</i>".equals(pText.substring(start, start + 4))) { //$NON-NLS-1$
                        italic = false;
                        start += 4;
                    } else if (pText.charAt(start) == '<') {
                        end = pText.indexOf('>', start + 1);
                        if (end < 0) {
                            end = pText.length();
                        } else {
                            end += 1;
                        }
                        start = end;
                    } else {
                        end = pText.indexOf('<', start);
                        if (end < 0)
                            end = pText.length();
                        int styleStart = pBuffer.length();
                        pBuffer.append(unescape(pText.substring(start, end)));
                        int styleEnd = pBuffer.length();
                        styles.add(newStyleRange(bold, italic, styleStart, styleEnd));
                        start = end;
                    }
                }

                if (pBuffer.length() == 0) {
                    top += PARAGRAPH_SPACING;
                } else {
                    StyledParagraph paragraph = new StyledParagraph();
                    paragraph.text = new TextLayout(getDisplay());
                    paragraph.text.setFont(getFont());
                    paragraph.text.setAlignment(SWT.LEFT);
                    paragraph.text.setSpacing(LINE_SPACING);
                    paragraph.text.setText(pBuffer.toString());
                    for (StyleRange style : styles) {
                        paragraph.text.setStyle(style, style.start, style.start + style.length);
                    }
                    paragraph.text.setWidth(wHint);
                    paragraph.top = top;
                    paragraphs.add(paragraph);
                    top += paragraph.text.getBounds().height + PARAGRAPH_SPACING;
                }
            }
        }
        return paragraphs.toArray(new StyledParagraph[paragraphs.size()]);
    }

    private StyledParagraph[] parseSimpleText(String text, int wHint) {
        List<StyledParagraph> paragraphs = new ArrayList<StyledLink.StyledParagraph>(text.length() / 2);
        int start = 0;
        int end = 0;
        int top = 0;
        StyledParagraph paragraph;
        while (start < text.length()) {
            if (text.charAt(start) == '\r') {
                if (start + 1 < text.length() && text.charAt(start + 1) == '\n') {
                    start += 2;
                } else {
                    start += 1;
                }
                top += PARAGRAPH_SPACING;
            } else if (text.charAt(start) == '\n') {
                start += 1;
                top += PARAGRAPH_SPACING;
            } else {
                end = Math.min(text.indexOf('\n', start), text.indexOf('\r', start));
                if (end < 0)
                    end = text.length();
                if (end > start) {
                    paragraph = new StyledParagraph();
                    paragraph.text = new TextLayout(getDisplay());
                    paragraph.text.setFont(getFont());
                    paragraph.text.setAlignment(SWT.LEFT);
                    paragraph.text.setSpacing(LINE_SPACING);
                    paragraph.text.setText(text.substring(start, end));
                    paragraph.text.setStyle(newStyle(false, false), 0, end - start);
                    paragraph.text.setWidth(wHint);
                    paragraph.top = top;
                    paragraphs.add(paragraph);
                    top += paragraph.text.getBounds().height;
                }
                start = end;
            }
        }
        return paragraphs.toArray(new StyledParagraph[paragraphs.size()]);
    }

    private String unescape(String text) {
        return EncodingUtils.unescape(text);
    }

    private StyleRange newStyleRange(boolean bold, boolean italic, int start, int end) {
        StyleRange style = new StyleRange();
        applyStyle(style, bold, italic);
        style.start = start;
        style.length = end - start;
        return style;
    }

    private TextStyle newStyle(boolean bold, boolean italic) {
        TextStyle style = new TextStyle();
        applyStyle(style, bold, italic);
        return style;
    }

    private void applyStyle(TextStyle style, boolean bold, boolean italic) {
        style.background = getBackground();
        style.foreground = getForeground();
        style.underline = isEnabled() && hovered;
        style.underlineColor = getForeground();
        style.underlineStyle = SWT.UNDERLINE_LINK;
        style.strikeout = false;
        style.strikeoutColor = null;
        if (bold && italic) {
            style.font = getBoldItalicFont();
        } else if (bold) {
            style.font = getBoldFont();
        } else if (italic) {
            style.font = getItalicFont();
        } else {
            style.font = getFont();
        }
    }

    private Font getBoldFont() {
        if (boldFont == null) {
            boldFont = new Font(getDisplay(), FontUtils.bold(getFont().getFontData(), true));
        }
        return boldFont;
    }

    private Font getItalicFont() {
        if (italicFont == null) {
            italicFont = new Font(getDisplay(), FontUtils.italic(getFont().getFontData(), true));
        }
        return italicFont;
    }

    private Font getBoldItalicFont() {
        if (boldItalicFont == null) {
            boldItalicFont = new Font(getDisplay(),
                    FontUtils.bold(FontUtils.italic(getFont().getFontData(), true), true));
        }
        return boldItalicFont;
    }

    private void drawParagraphs(GC gc) {
        ensureParagraphs(cachedWidth);
        gc.setAntialias(SWT.ON);
        gc.setTextAntialias(SWT.ON);

        for (StyledParagraph p : paragraphs) {
            p.text.draw(gc, 0, p.top);
        }
    }

    private void setHovered(boolean hovered) {
        hovered = hovered && isEnabled();
        if (hovered == this.hovered)
            return;
        this.hovered = hovered;
        releaseLines();
        redraw();
    }

    @Override
    public Point computeSize(int wHint, int hHint, boolean changed) {
        checkWidget();
        if (changed || cachedWidth != wHint || paragraphs == null || cachedSize == null) {
            releaseLines();
            ensureParagraphs(wHint);
            cachedWidth = wHint;
        }
        return new Point(cachedSize.x, cachedSize.y);
    }

    @Override
    public void dispose() {
        releaseLines();
        if (boldFont != null) {
            boldFont.dispose();
            boldFont = null;
        }
        if (italicFont != null) {
            italicFont.dispose();
            italicFont = null;
        }
        if (boldItalicFont != null) {
            boldItalicFont.dispose();
            boldItalicFont = null;
        }
        super.dispose();
    }

    public void setText(String text) {
        checkWidget();
        if (text == null)
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        if (text.equals(this.text))
            return;
        this.text = text;
        releaseLines();
        redraw();
    }

    public String getText() {
        return text;
    }

}