package net.sourceforge.pmd.eclipse.ui; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sourceforge.pmd.eclipse.ui.editors.StyleExtractor; import net.sourceforge.pmd.eclipse.ui.editors.SyntaxData; import net.sourceforge.pmd.eclipse.ui.editors.SyntaxManager; import net.sourceforge.pmd.eclipse.ui.preferences.editors.SWTUtil; import net.sourceforge.pmd.eclipse.util.FontBuilder; import net.sourceforge.pmd.lang.Language; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.TextStyle; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.browser.IWebBrowser; /** * * @author Brian Remedios */ public class PageBuilder { private List<int[]> headingSpans = new ArrayList<int[]>(); private List<int[]> codeSpans = new ArrayList<int[]>(); private Map<int[], String> linksBySpan = new HashMap<int[], String>(); private StringBuilder buffer; private Color headingColor; private int indentDepth; private TextStyle codeStyle; private StyleExtractor codeStyleExtractor; private static final char CR = '\n'; private static final Color BACKGROUND = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE); private static final Comparator<StyleRange> StyleComparator = new Comparator<StyleRange>() { public int compare(StyleRange sr1, StyleRange sr2) { return sr1.start - sr2.start; } }; public PageBuilder(int textIndent, int headingColorIndex, FontBuilder codeFontBuilder) { buffer = new StringBuilder(500); indentDepth = textIndent; Display display = Display.getCurrent(); headingColor = display.getSystemColor(headingColorIndex); codeStyle = codeFontBuilder.style(display); SyntaxData syntax = SyntaxManager.getSyntaxData("java"); codeStyleExtractor = new StyleExtractor(syntax); } public static StyleRange[] sort(List<StyleRange> ranges) { StyleRange[] styles = ranges.toArray(new StyleRange[ranges.size()]); Arrays.sort(styles, StyleComparator); return styles; } public void indentDepth(int aDepth) { indentDepth = aDepth; } public int indentDepth() { return indentDepth; } public boolean hasLinks() { return !linksBySpan.isEmpty(); } public void clear() { buffer.setLength(0); if (headingSpans != null) { headingSpans.clear(); } if (codeSpans != null) { codeSpans.clear(); } if (linksBySpan != null) { linksBySpan.clear(); } } public void setLanguage(Language language) { SyntaxData syntax = SyntaxManager.getSyntaxData(language.getTerseName()); codeStyleExtractor.syntax(syntax); } public void addText(String text) { for (int i=0; i<indentDepth; i++) buffer.append(' '); buffer.append(text).append(CR); } public void addRawText(String text) { buffer.append(text); } public void addHeading(String headingKey) { String heading = SWTUtil.stringFor(headingKey); int length = buffer.length(); if (length > 0) { buffer.append(CR); length += 1; } headingSpans.add( new int[] { length, length + heading.length() } ); buffer.append(heading).append(CR); } public void addCode(String code) { int length = buffer.length(); codeSpans.add( new int[] { length, length + code.length() } ); buffer.append(code); } public void addLink(String text, String link) { int length = buffer.length(); linksBySpan.put( new int[] { length, length + text.length() }, link ); buffer.append(text); } private String linkAt(int textIndex) { int[] span; for (Map.Entry<int[], String> entry : linksBySpan.entrySet()) { span = entry.getKey(); if (span[0] <= textIndex && textIndex <= span[1]) { return entry.getValue(); } } return null; } public void showOn(StyledText widget) { String text = buffer.toString(); widget.setText(text); List<StyleRange> ranges = new ArrayList<StyleRange>(); int[] span; for (int i=0; i<headingSpans.size(); i++) { span = headingSpans.get(i); ranges.add( new StyleRange(span[0], span[1]-span[0], headingColor, BACKGROUND, SWT.BOLD) ); } for (int[] spn : linksBySpan.keySet()) { StyleRange style = new StyleRange(spn[0], spn[1]-spn[0], headingColor, BACKGROUND, SWT.UNDERLINE_LINK); style.underline = true; ranges.add(style); } String crStr = Character.toString(CR); StyleRange sr; for (int i=0; i<codeSpans.size(); i++) { span = codeSpans.get(i); sr = new StyleRange(codeStyle); sr.start = span[0]; sr.length = span[1]-span[0]; // ranges.add(sr); TODO wtf? causes crashes List<StyleRange> colorRanges = codeStyleExtractor.stylesFor(text, sr.start, sr.length, crStr); for (StyleRange range : colorRanges) { ranges.add(range); } } StyleRange[] styles = sort(ranges); // must be in order! widget.setStyleRanges(styles); } public void addLinkHandler(final StyledText widget) { widget.addListener(SWT.MouseDown, new Listener() { public void handleEvent(Event event) { // It is up to the application to determine when and how a link should be activated. // In this snippet links are activated on mouse down when the control key is held down // if ((event.stateMask & SWT.MOD1) != 0) { try { int offset = widget.getOffsetAtLocation(new Point (event.x, event.y)); String link = linkAt(offset); if (link != null) { launchBrowser(link); } } catch (IllegalArgumentException e) { // no character under event.x, event.y } } // } }); } private static void launchBrowser(String link) { try { IWebBrowser browser = PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser(); browser.openURL(new URL(link)); } catch (Exception ex) { ex.printStackTrace(); } } }