Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDani Megert2006-09-22 15:17:39 +0000
committerDani Megert2006-09-22 15:17:39 +0000
commitb3b4d7bbb5e53949ead3ed5da6bdf902a9d4ebbb (patch)
tree86645f6b1bf72a921e686db1a84b5a7ea9cf69bb /org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html
parent1932e846bc3e2eaa301b5ec4094d14c42103a281 (diff)
downloadeclipse.platform.text-b3b4d7bbb5e53949ead3ed5da6bdf902a9d4ebbb.tar.gz
eclipse.platform.text-b3b4d7bbb5e53949ead3ed5da6bdf902a9d4ebbb.tar.xz
eclipse.platform.text-b3b4d7bbb5e53949ead3ed5da6bdf902a9d4ebbb.zip
Fixed bug: 157461: [implementation] Put all html rendering related classes into one package and declare others as x-friends
Diffstat (limited to 'org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html')
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/BrowserInformationControl.java592
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTML2TextReader.java324
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLMessages.java81
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLMessages.properties16
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLPrinter.java301
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLTextPresenter.java203
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/SingleCharReader.java65
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/SubstitutionTextReader.java159
8 files changed, 1741 insertions, 0 deletions
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/BrowserInformationControl.java b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/BrowserInformationControl.java
new file mode 100644
index 00000000000..bafa4810c1e
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/BrowserInformationControl.java
@@ -0,0 +1,592 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.internal.text.html;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Iterator;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.browser.LocationAdapter;
+import org.eclipse.swt.browser.LocationEvent;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+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.FontData;
+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.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.runtime.ListenerList;
+
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlExtension;
+import org.eclipse.jface.text.IInformationControlExtension3;
+import org.eclipse.jface.text.TextPresentation;
+
+
+/**
+ * Displays textual information in a {@link org.eclipse.swt.browser.Browser} widget.
+ * <p>
+ * Moved into this package from <code>org.eclipse.jface.internal.text.revisions</code>.</p>
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.</p>
+ * <p>
+ * Current problems:
+ * <ul>
+ * <li>the size computation is too small</li>
+ * <li>focusLost event is not sent - see https://bugs.eclipse.org/bugs/show_bug.cgi?id=84532</li>
+ * <li>we need to call <code>content.replaceAll("overflow: auto;", "")<code> which is a problem if <code>"overflow: auto;"</code> appears in the content</li>
+ * </ul>
+ * </p>
+ *
+ * @since 3.2
+ */
+public class BrowserInformationControl implements IInformationControl, IInformationControlExtension, IInformationControlExtension3, DisposeListener {
+
+
+ /**
+ * Tells whether the SWT Browser widget and hence this information
+ * control is available.
+ *
+ * @param parent the parent component used for checking or <code>null</code> if none
+ * @return <code>true</code> if this control is available
+ */
+ public static boolean isAvailable(Composite parent) {
+ if (!fgAvailabilityChecked) {
+ try {
+ Browser browser= new Browser(parent, SWT.NONE);
+ browser.dispose();
+ fgIsAvailable= true;
+ } catch (SWTError er) {
+ fgIsAvailable= false;
+ } finally {
+ fgAvailabilityChecked= true;
+ }
+ }
+
+ return fgIsAvailable;
+ }
+
+
+ /** Border thickness in pixels. */
+ private static final int BORDER= 1;
+
+ /**
+ * Minimal size constraints.
+ * @since 3.2
+ */
+ private static final int MIN_WIDTH= 80;
+ private static final int MIN_HEIGHT= 80;
+
+
+ /**
+ * Availability checking cache.
+ */
+ private static boolean fgIsAvailable= false;
+ private static boolean fgAvailabilityChecked= false;
+
+ /** The control's shell */
+ private Shell fShell;
+ /** The control's browser widget */
+ private Browser fBrowser;
+ /** Tells whether the browser has content */
+ private boolean fBrowserHasContent;
+ /** The control width constraint */
+ private int fMaxWidth= SWT.DEFAULT;
+ /** The control height constraint */
+ private int fMaxHeight= SWT.DEFAULT;
+ private Font fStatusTextFont;
+ private Label fStatusTextField;
+ private String fStatusFieldText;
+ private boolean fHideScrollBars;
+ private Listener fDeactivateListener;
+ private ListenerList fFocusListeners= new ListenerList();
+ private Label fSeparator;
+ private String fInputText;
+ private TextLayout fTextLayout;
+
+ private TextStyle fBoldStyle;
+
+ /**
+ * Creates a default information control with the given shell as parent. The given
+ * information presenter is used to process the information to be displayed. The given
+ * styles are applied to the created styled text widget.
+ *
+ * @param parent the parent shell
+ * @param shellStyle the additional styles for the shell
+ * @param style the additional styles for the styled text widget
+ */
+ public BrowserInformationControl(Shell parent, int shellStyle, int style) {
+ this(parent, shellStyle, style, null);
+ }
+
+ /**
+ * Creates a default information control with the given shell as parent. The given
+ * information presenter is used to process the information to be displayed. The given
+ * styles are applied to the created styled text widget.
+ *
+ * @param parent the parent shell
+ * @param shellStyle the additional styles for the shell
+ * @param style the additional styles for the styled text widget
+ * @param statusFieldText the text to be used in the optional status field
+ * or <code>null</code> if the status field should be hidden
+ */
+ public BrowserInformationControl(Shell parent, int shellStyle, int style, String statusFieldText) {
+ fStatusFieldText= statusFieldText;
+
+ fShell= new Shell(parent, SWT.NO_FOCUS | SWT.ON_TOP | shellStyle);
+ Display display= fShell.getDisplay();
+ fShell.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
+ fTextLayout= new TextLayout(display);
+
+ Composite composite= fShell;
+ GridLayout layout= new GridLayout(1, false);
+ int border= ((shellStyle & SWT.NO_TRIM) == 0) ? 0 : BORDER;
+ layout.marginHeight= border;
+ layout.marginWidth= border;
+ composite.setLayout(layout);
+
+ if (statusFieldText != null) {
+ composite= new Composite(composite, SWT.NONE);
+ layout= new GridLayout(1, false);
+ layout.marginHeight= 0;
+ layout.marginWidth= 0;
+ layout.verticalSpacing= 1;
+ layout.horizontalSpacing= 1;
+ composite.setLayout(layout);
+
+ GridData gd= new GridData(GridData.FILL_BOTH);
+ composite.setLayoutData(gd);
+
+ composite.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+ composite.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+ }
+
+ // Browser field
+ fBrowser= new Browser(composite, SWT.NONE);
+ fHideScrollBars= (style & SWT.V_SCROLL) == 0 && (style & SWT.H_SCROLL) == 0;
+
+ GridData gd= new GridData(GridData.BEGINNING | GridData.FILL_BOTH);
+ fBrowser.setLayoutData(gd);
+
+ fBrowser.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+ fBrowser.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+ fBrowser.addKeyListener(new KeyListener() {
+
+ public void keyPressed(KeyEvent e) {
+ if (e.character == 0x1B) // ESC
+ fShell.dispose();
+ }
+
+ public void keyReleased(KeyEvent e) {}
+ });
+ /*
+ * XXX revisit when the Browser support is better
+ * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=107629. Choosing a link to a
+ * non-available target will show an error dialog behind the ON_TOP shell that seemingly
+ * blocks the workbench. Disable links completely for now.
+ */
+ fBrowser.addLocationListener(new LocationAdapter() {
+ /*
+ * @see org.eclipse.swt.browser.LocationAdapter#changing(org.eclipse.swt.browser.LocationEvent)
+ */
+ public void changing(LocationEvent event) {
+ String location= event.location;
+ /*
+ * Using the Browser.setText API triggers a location change to "about:blank" with
+ * the mozilla widget. The Browser on carbon uses yet another kind of special
+ * initialization URLs.
+ * TODO remove this code once https://bugs.eclipse.org/bugs/show_bug.cgi?id=130314 is fixed
+ */
+ if (!"about:blank".equals(location) && !("carbon".equals(SWT.getPlatform()) && location.startsWith("applewebdata:"))) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ event.doit= false;
+ }
+ });
+
+ // Replace browser's built-in context menu with none
+ fBrowser.setMenu(new Menu(fShell, SWT.NONE));
+
+ // Status field
+ if (statusFieldText != null) {
+
+ fSeparator= new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.LINE_DOT);
+ fSeparator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ // Status field label
+ fStatusTextField= new Label(composite, SWT.RIGHT);
+ fStatusTextField.setText(statusFieldText);
+ Font font= fStatusTextField.getFont();
+ FontData[] fontDatas= font.getFontData();
+ for (int i= 0; i < fontDatas.length; i++)
+ fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10);
+ fStatusTextFont= new Font(fStatusTextField.getDisplay(), fontDatas);
+ fStatusTextField.setFont(fStatusTextFont);
+ gd= new GridData(GridData.FILL_HORIZONTAL | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.VERTICAL_ALIGN_BEGINNING);
+ fStatusTextField.setLayoutData(gd);
+
+ fStatusTextField.setForeground(display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW));
+
+ fStatusTextField.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+ }
+
+ addDisposeListener(this);
+ createTextLayout();
+ }
+
+ /**
+ * Creates a default information control with the given shell as parent. The given
+ * information presenter is used to process the information to be displayed. The given
+ * styles are applied to the created styled text widget.
+ *
+ * @param parent the parent shell
+ * @param style the additional styles for the browser widget
+ */
+ public BrowserInformationControl(Shell parent,int style) {
+ this(parent, SWT.TOOL | SWT.NO_TRIM, style);
+ }
+
+ /**
+ * Creates a default information control with the given shell as parent.
+ * No information presenter is used to process the information
+ * to be displayed. No additional styles are applied to the styled text widget.
+ *
+ * @param parent the parent shell
+ */
+ public BrowserInformationControl(Shell parent) {
+ this(parent, SWT.NONE);
+ }
+
+
+ /*
+ * @see IInformationControl#setInformation(String)
+ */
+ public void setInformation(String content) {
+ fBrowserHasContent= content != null && content.length() > 0;
+
+ if (!fBrowserHasContent)
+ content= "<html><body ></html>"; //$NON-NLS-1$
+
+ fInputText= content;
+
+ int shellStyle= fShell.getStyle();
+ boolean RTL= (shellStyle & SWT.RIGHT_TO_LEFT) != 0;
+
+ String[] styles= null;
+ if (RTL && !fHideScrollBars)
+ styles= new String[] { "direction:rtl;", "word-wrap:break-word;" }; //$NON-NLS-1$ //$NON-NLS-2$
+ else if (RTL && fHideScrollBars)
+ styles= new String[] { "direction:rtl;", "overflow:hidden;", "word-wrap:break-word;" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ else if (fHideScrollBars && true)
+ styles= new String[] { "overflow:hidden;", "word-wrap: break-word;" }; //$NON-NLS-1$ //$NON-NLS-2$
+
+ // XXX: this is a hack but needed to ensure our overflow directive is honored
+ content= content.replaceAll("overflow: auto;", ""); //$NON-NLS-1$ //$NON-NLS-2$
+
+ if (styles != null) {
+ StringBuffer buffer= new StringBuffer(content);
+ HTMLPrinter.insertStyles(buffer, styles);
+ content= buffer.toString();
+ }
+
+ fBrowser.setText(content);
+
+ }
+
+ /*
+ * @see org.eclipse.jdt.internal.ui.text.IInformationControlExtension4#setStatusText(java.lang.String)
+ * @since 3.2
+ */
+ public void setStatusText(String statusFieldText) {
+ fStatusFieldText= statusFieldText;
+ }
+
+ /*
+ * @see IInformationControl#setVisible(boolean)
+ */
+ public void setVisible(boolean visible) {
+ if (fShell.isVisible() == visible)
+ return;
+
+ if (visible) {
+ if (fStatusTextField != null) {
+ boolean state= fStatusFieldText != null;
+ if (state)
+ fStatusTextField.setText(fStatusFieldText);
+ fStatusTextField.setVisible(state);
+ fSeparator.setVisible(state);
+ }
+ }
+
+ fShell.setVisible(visible);
+ if (!visible)
+ setInformation(""); //$NON-NLS-1$
+ }
+
+ /**
+ * Creates and initializes the text layout used
+ * to compute the size hint.
+ *
+ * @since 3.2
+ */
+ private void createTextLayout() {
+ fTextLayout= new TextLayout(fBrowser.getDisplay());
+
+ // Initialize fonts
+ Font font= fBrowser.getFont();
+ fTextLayout.setFont(font);
+ fTextLayout.setWidth(-1);
+ FontData[] fontData= font.getFontData();
+ for (int i= 0; i < fontData.length; i++)
+ fontData[i].setStyle(SWT.BOLD);
+ font= new Font(fShell.getDisplay(), fontData);
+ fBoldStyle= new TextStyle(font, null, null);
+
+ // Compute and set tab width
+ fTextLayout.setText(" "); //$NON-NLS-1$
+ int tabWidth = fTextLayout.getBounds().width;
+ fTextLayout.setTabs(new int[] {tabWidth});
+
+ fTextLayout.setText(""); //$NON-NLS-1$
+ }
+
+ /*
+ * @see IInformationControl#dispose()
+ */
+ public void dispose() {
+ fTextLayout.dispose();
+ fTextLayout= null;
+ fBoldStyle.font.dispose();
+ fBoldStyle= null;
+ if (fShell != null && !fShell.isDisposed())
+ fShell.dispose();
+ else
+ widgetDisposed(null);
+ }
+
+ /*
+ * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
+ */
+ public void widgetDisposed(DisposeEvent event) {
+ if (fStatusTextFont != null && !fStatusTextFont.isDisposed())
+ fStatusTextFont.dispose();
+
+ fShell= null;
+ fBrowser= null;
+ fStatusTextFont= null;
+ }
+
+ /*
+ * @see IInformationControl#setSize(int, int)
+ */
+ public void setSize(int width, int height) {
+ fShell.setSize(Math.min(width, fMaxWidth), Math.min(height, fMaxHeight));
+ }
+
+ /*
+ * @see IInformationControl#setLocation(Point)
+ */
+ public void setLocation(Point location) {
+ fShell.setLocation(location);
+ }
+
+ /*
+ * @see IInformationControl#setSizeConstraints(int, int)
+ */
+ public void setSizeConstraints(int maxWidth, int maxHeight) {
+ fMaxWidth= maxWidth;
+ fMaxHeight= maxHeight;
+ }
+
+ /*
+ * @see IInformationControl#computeSizeHint()
+ */
+ public Point computeSizeHint() {
+ TextPresentation presentation= new TextPresentation();
+ HTML2TextReader reader= new HTML2TextReader(new StringReader(fInputText), presentation);
+ String text;
+ try {
+ text= reader.getString();
+ } catch (IOException e) {
+ text= ""; //$NON-NLS-1$
+ }
+
+ fTextLayout.setText(text);
+ Iterator iter= presentation.getAllStyleRangeIterator();
+ while (iter.hasNext()) {
+ StyleRange sr= (StyleRange)iter.next();
+ if (sr.fontStyle == SWT.BOLD)
+ fTextLayout.setStyle(fBoldStyle, sr.start, sr.start + sr.length - 1);
+ }
+ Rectangle bounds= fTextLayout.getBounds();
+ int width= bounds.width;
+ int height= bounds.height;
+
+ width += 15;
+ height += 25;
+
+ if (fStatusFieldText != null && fSeparator != null) {
+ fTextLayout.setText(fStatusFieldText);
+ Rectangle statusBounds= fTextLayout.getBounds();
+ Rectangle separatorBounds= fSeparator.getBounds();
+ width= Math.max(width, statusBounds.width);
+ height= height + statusBounds.height + separatorBounds.height;
+ }
+
+ // Apply size constraints
+ if (fMaxWidth != SWT.DEFAULT)
+ width= Math.min(fMaxWidth, width);
+ if (fMaxHeight != SWT.DEFAULT)
+ height= Math.min(fMaxHeight, height);
+
+ // Ensure minimal size
+ width= Math.max(MIN_WIDTH, width);
+ height= Math.max(MIN_HEIGHT, height);
+
+ return new Point(width, height);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IInformationControlExtension3#computeTrim()
+ */
+ public Rectangle computeTrim() {
+ return fShell.computeTrim(0, 0, 0, 0);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IInformationControlExtension3#getBounds()
+ */
+ public Rectangle getBounds() {
+ return fShell.getBounds();
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IInformationControlExtension3#restoresLocation()
+ */
+ public boolean restoresLocation() {
+ return false;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IInformationControlExtension3#restoresSize()
+ */
+ public boolean restoresSize() {
+ return false;
+ }
+
+ /*
+ * @see IInformationControl#addDisposeListener(DisposeListener)
+ */
+ public void addDisposeListener(DisposeListener listener) {
+ fShell.addDisposeListener(listener);
+ }
+
+ /*
+ * @see IInformationControl#removeDisposeListener(DisposeListener)
+ */
+ public void removeDisposeListener(DisposeListener listener) {
+ fShell.removeDisposeListener(listener);
+ }
+
+ /*
+ * @see IInformationControl#setForegroundColor(Color)
+ */
+ public void setForegroundColor(Color foreground) {
+ fBrowser.setForeground(foreground);
+ }
+
+ /*
+ * @see IInformationControl#setBackgroundColor(Color)
+ */
+ public void setBackgroundColor(Color background) {
+ fBrowser.setBackground(background);
+ }
+
+ /*
+ * @see IInformationControl#isFocusControl()
+ */
+ public boolean isFocusControl() {
+ return fBrowser.isFocusControl();
+ }
+
+ /*
+ * @see IInformationControl#setFocus()
+ */
+ public void setFocus() {
+ fShell.forceFocus();
+ fBrowser.setFocus();
+ }
+
+ /*
+ * @see IInformationControl#addFocusListener(FocusListener)
+ */
+ public void addFocusListener(final FocusListener listener) {
+ fBrowser.addFocusListener(listener);
+
+ /*
+ * FIXME: This is a workaround for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=84532
+ * (Browser widget does not send focusLost event)
+ */
+ if (fFocusListeners.isEmpty()) {
+ fDeactivateListener= new Listener() {
+ public void handleEvent(Event event) {
+ Object[] listeners= fFocusListeners.getListeners();
+ for (int i = 0; i < listeners.length; i++)
+ ((FocusListener)listeners[i]).focusLost(new FocusEvent(event));
+ }
+ };
+ fBrowser.getShell().addListener(SWT.Deactivate, fDeactivateListener);
+ }
+ fFocusListeners.add(listener);
+ }
+
+ /*
+ * @see IInformationControl#removeFocusListener(FocusListener)
+ */
+ public void removeFocusListener(FocusListener listener) {
+ fBrowser.removeFocusListener(listener);
+
+ /*
+ * FIXME: This is a workaround for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=84532
+ * (Browser widget does not send focusLost event)
+ */
+ fFocusListeners.remove(listener);
+ if (fFocusListeners.isEmpty()) {
+ fBrowser.getShell().removeListener(SWT.Deactivate, fDeactivateListener);
+ fDeactivateListener= null;
+ }
+ }
+
+ /*
+ * @see IInformationControlExtension#hasContents()
+ */
+ public boolean hasContents() {
+ return fBrowserHasContent;
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTML2TextReader.java b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTML2TextReader.java
new file mode 100644
index 00000000000..3c32cbc907c
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTML2TextReader.java
@@ -0,0 +1,324 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.internal.text.html;
+
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.io.Reader;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+
+import org.eclipse.jface.text.TextPresentation;
+
+
+/**
+ * Reads the text contents from a reader of HTML contents and translates
+ * the tags or cut them out.
+ * <p>
+ * Moved into this package from <code>org.eclipse.jface.internal.text.revisions</code>.</p>
+ */
+public class HTML2TextReader extends SubstitutionTextReader {
+
+ private static final String EMPTY_STRING= ""; //$NON-NLS-1$
+ private static final Map fgEntityLookup;
+ private static final Set fgTags;
+
+ static {
+
+ fgTags= new HashSet();
+ fgTags.add("b"); //$NON-NLS-1$
+ fgTags.add("br"); //$NON-NLS-1$
+ fgTags.add("br/"); //$NON-NLS-1$
+ fgTags.add("div"); //$NON-NLS-1$
+ fgTags.add("h1"); //$NON-NLS-1$
+ fgTags.add("h2"); //$NON-NLS-1$
+ fgTags.add("h3"); //$NON-NLS-1$
+ fgTags.add("h4"); //$NON-NLS-1$
+ fgTags.add("h5"); //$NON-NLS-1$
+ fgTags.add("p"); //$NON-NLS-1$
+ fgTags.add("dl"); //$NON-NLS-1$
+ fgTags.add("dt"); //$NON-NLS-1$
+ fgTags.add("dd"); //$NON-NLS-1$
+ fgTags.add("li"); //$NON-NLS-1$
+ fgTags.add("ul"); //$NON-NLS-1$
+ fgTags.add("pre"); //$NON-NLS-1$
+ fgTags.add("head"); //$NON-NLS-1$
+
+ fgEntityLookup= new HashMap(7);
+ fgEntityLookup.put("lt", "<"); //$NON-NLS-1$ //$NON-NLS-2$
+ fgEntityLookup.put("gt", ">"); //$NON-NLS-1$ //$NON-NLS-2$
+ fgEntityLookup.put("nbsp", " "); //$NON-NLS-1$ //$NON-NLS-2$
+ fgEntityLookup.put("amp", "&"); //$NON-NLS-1$ //$NON-NLS-2$
+ fgEntityLookup.put("circ", "^"); //$NON-NLS-1$ //$NON-NLS-2$
+ fgEntityLookup.put("tilde", "~"); //$NON-NLS-2$ //$NON-NLS-1$
+ fgEntityLookup.put("quot", "\""); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ private int fCounter= 0;
+ private TextPresentation fTextPresentation;
+ private int fBold= 0;
+ private int fStartOffset= -1;
+ private boolean fInParagraph= false;
+ private boolean fIsPreformattedText= false;
+ private boolean fIgnore= false;
+
+ /**
+ * Transforms the HTML text from the reader to formatted text.
+ *
+ * @param reader the reader
+ * @param presentation If not <code>null</code>, formattings will be applied to
+ * the presentation.
+ */
+ public HTML2TextReader(Reader reader, TextPresentation presentation) {
+ super(new PushbackReader(reader));
+ fTextPresentation= presentation;
+ }
+
+ public int read() throws IOException {
+ int c= super.read();
+ if (c != -1)
+ ++ fCounter;
+ return c;
+ }
+
+ protected void startBold() {
+ if (fBold == 0)
+ fStartOffset= fCounter;
+ ++ fBold;
+ }
+
+ protected void startPreformattedText() {
+ fIsPreformattedText= true;
+ setSkipWhitespace(false);
+ }
+
+ protected void stopPreformattedText() {
+ fIsPreformattedText= false;
+ setSkipWhitespace(true);
+ }
+
+ protected void stopBold() {
+ -- fBold;
+ if (fBold == 0) {
+ if (fTextPresentation != null) {
+ fTextPresentation.addStyleRange(new StyleRange(fStartOffset, fCounter - fStartOffset, null, null, SWT.BOLD));
+ }
+ fStartOffset= -1;
+ }
+ }
+
+ /*
+ * @see org.eclipse.jdt.internal.ui.text.SubstitutionTextReader#computeSubstitution(int)
+ */
+ protected String computeSubstitution(int c) throws IOException {
+
+ if (c == '<')
+ return processHTMLTag();
+ else if (fIgnore)
+ return EMPTY_STRING;
+ else if (c == '&')
+ return processEntity();
+ else if (fIsPreformattedText)
+ return processPreformattedText(c);
+
+ return null;
+ }
+
+ private String html2Text(String html) {
+
+ if (html == null || html.length() == 0)
+ return EMPTY_STRING;
+
+ html= html.toLowerCase();
+
+ String tag= html;
+ if ('/' == tag.charAt(0))
+ tag= tag.substring(1);
+
+ if (!fgTags.contains(tag))
+ return EMPTY_STRING;
+
+
+ if ("pre".equals(html)) { //$NON-NLS-1$
+ startPreformattedText();
+ return EMPTY_STRING;
+ }
+
+ if ("/pre".equals(html)) { //$NON-NLS-1$
+ stopPreformattedText();
+ return EMPTY_STRING;
+ }
+
+ if (fIsPreformattedText)
+ return EMPTY_STRING;
+
+ if ("b".equals(html)) { //$NON-NLS-1$
+ startBold();
+ return EMPTY_STRING;
+ }
+
+ if ((html.length() > 1 && html.charAt(0) == 'h' && Character.isDigit(html.charAt(1))) || "dt".equals(html)) { //$NON-NLS-1$
+ startBold();
+ return EMPTY_STRING;
+ }
+
+ if ("dl".equals(html)) //$NON-NLS-1$
+ return LINE_DELIM;
+
+ if ("dd".equals(html)) //$NON-NLS-1$
+ return "\t"; //$NON-NLS-1$
+
+ if ("li".equals(html)) //$NON-NLS-1$
+ // FIXME: this hard-coded prefix does not work for RTL languages, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=91682
+ return LINE_DELIM + HTMLMessages.getString("HTML2TextReader.listItemPrefix"); //$NON-NLS-1$
+
+ if ("/b".equals(html)) { //$NON-NLS-1$
+ stopBold();
+ return EMPTY_STRING;
+ }
+
+ if ("p".equals(html)) { //$NON-NLS-1$
+ fInParagraph= true;
+ return LINE_DELIM;
+ }
+
+ if ("br".equals(html) || "br/".equals(html) || "div".equals(html)) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ return LINE_DELIM;
+
+ if ("/p".equals(html)) { //$NON-NLS-1$
+ boolean inParagraph= fInParagraph;
+ fInParagraph= false;
+ return inParagraph ? EMPTY_STRING : LINE_DELIM;
+ }
+
+ if ((html.startsWith("/h") && html.length() > 2 && Character.isDigit(html.charAt(2))) || "/dt".equals(html)) { //$NON-NLS-1$ //$NON-NLS-2$
+ stopBold();
+ return LINE_DELIM;
+ }
+
+ if ("/dd".equals(html)) //$NON-NLS-1$
+ return LINE_DELIM;
+
+ if ("head".equals(html)) { //$NON-NLS-1$
+ fIgnore= true;
+ return EMPTY_STRING;
+ }
+
+ if ("/head".equals(html)) { //$NON-NLS-1$
+ fIgnore= false;
+ return EMPTY_STRING;
+ }
+
+ return EMPTY_STRING;
+ }
+
+ /*
+ * A '<' has been read. Process a html tag
+ */
+ private String processHTMLTag() throws IOException {
+
+ StringBuffer buf= new StringBuffer();
+ int ch;
+ do {
+
+ ch= nextChar();
+
+ while (ch != -1 && ch != '>') {
+ buf.append(Character.toLowerCase((char) ch));
+ ch= nextChar();
+ if (ch == '"'){
+ buf.append(Character.toLowerCase((char) ch));
+ ch= nextChar();
+ while (ch != -1 && ch != '"'){
+ buf.append(Character.toLowerCase((char) ch));
+ ch= nextChar();
+ }
+ }
+ if (ch == '<'){
+ unread(ch);
+ return '<' + buf.toString();
+ }
+ }
+
+ if (ch == -1)
+ return null;
+
+ int tagLen= buf.length();
+ // needs special treatment for comments
+ if ((tagLen >= 3 && "!--".equals(buf.substring(0, 3))) //$NON-NLS-1$
+ && !(tagLen >= 5 && "--".equals(buf.substring(tagLen - 2)))) { //$NON-NLS-1$
+ // unfinished comment
+ buf.append(ch);
+ } else {
+ break;
+ }
+ } while (true);
+
+ return html2Text(buf.toString());
+ }
+
+ private String processPreformattedText(int c) {
+ if (c == '\r' || c == '\n')
+ fCounter++;
+ return null;
+ }
+
+
+ private void unread(int ch) throws IOException {
+ ((PushbackReader) getReader()).unread(ch);
+ }
+
+ protected String entity2Text(String symbol) {
+ if (symbol.length() > 1 && symbol.charAt(0) == '#') {
+ int ch;
+ try {
+ if (symbol.charAt(1) == 'x') {
+ ch= Integer.parseInt(symbol.substring(2), 16);
+ } else {
+ ch= Integer.parseInt(symbol.substring(1), 10);
+ }
+ return EMPTY_STRING + (char)ch;
+ } catch (NumberFormatException e) {
+ }
+ } else {
+ String str= (String) fgEntityLookup.get(symbol);
+ if (str != null) {
+ return str;
+ }
+ }
+ return "&" + symbol; // not found //$NON-NLS-1$
+ }
+
+ /*
+ * A '&' has been read. Process a entity
+ */
+ private String processEntity() throws IOException {
+ StringBuffer buf= new StringBuffer();
+ int ch= nextChar();
+ while (Character.isLetterOrDigit((char)ch) || ch == '#') {
+ buf.append((char) ch);
+ ch= nextChar();
+ }
+
+ if (ch == ';')
+ return entity2Text(buf.toString());
+
+ buf.insert(0, '&');
+ if (ch != -1)
+ buf.append((char) ch);
+ return buf.toString();
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLMessages.java b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLMessages.java
new file mode 100644
index 00000000000..69679ccf543
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLMessages.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2006 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.internal.text.html;
+
+import com.ibm.icu.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+
+/**
+ * Helper class to get NLSed messages.
+ *
+ * @since 3.3
+ */
+class HTMLMessages {
+
+ private static final String RESOURCE_BUNDLE= HTMLMessages.class.getName();
+
+ private static ResourceBundle fgResourceBundle= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+
+ private HTMLMessages() {
+ }
+
+ /**
+ * Gets a string from the resource bundle.
+ *
+ * @param key the string used to get the bundle value, must not be null
+ * @return the string from the resource bundle
+ */
+ public static String getString(String key) {
+ try {
+ return fgResourceBundle.getString(key);
+ } catch (MissingResourceException e) {
+ return "!" + key + "!";//$NON-NLS-2$ //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Gets a string from the resource bundle and formats it with the given arguments.
+ *
+ * @param key the string used to get the bundle value, must not be null
+ * @param args the arguments used to format the string
+ * @return the formatted string
+ */
+ public static String getFormattedString(String key, Object[] args) {
+ String format= null;
+ try {
+ format= fgResourceBundle.getString(key);
+ } catch (MissingResourceException e) {
+ return "!" + key + "!";//$NON-NLS-2$ //$NON-NLS-1$
+ }
+ return MessageFormat.format(format, args);
+ }
+
+ /**
+ * Gets a string from the resource bundle and formats it with the given argument.
+ *
+ * @param key the string used to get the bundle value, must not be null
+ * @param arg the argument used to format the string
+ * @return the formatted string
+ */
+ public static String getFormattedString(String key, Object arg) {
+ String format= null;
+ try {
+ format= fgResourceBundle.getString(key);
+ } catch (MissingResourceException e) {
+ return "!" + key + "!";//$NON-NLS-2$ //$NON-NLS-1$
+ }
+ if (arg == null)
+ arg= ""; //$NON-NLS-1$
+ return MessageFormat.format(format, new Object[] { arg });
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLMessages.properties b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLMessages.properties
new file mode 100644
index 00000000000..da851b14949
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLMessages.properties
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 2006 IBM Corporation and others.
+# 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:
+# IBM Corporation - initial API and implementation
+###############################################################################
+
+
+HTMLTextPresenter.ellipse= ...
+
+# The following property value must end with a space
+HTML2TextReader.listItemPrefix=\t-\
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLPrinter.java b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLPrinter.java
new file mode 100644
index 00000000000..e6e891b66a9
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLPrinter.java
@@ -0,0 +1,301 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.internal.text.html;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.net.URL;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+
+
+/**
+ * Provides a set of convenience methods for creating HTML pages.
+ * <p>
+ * Moved into this package from <code>org.eclipse.jface.internal.text.revisions</code>.</p>
+ */
+public class HTMLPrinter {
+
+ private static RGB BG_COLOR_RGB= null;
+
+ static {
+ final Display display= Display.getDefault();
+ if (display != null && !display.isDisposed()) {
+ try {
+ display.asyncExec(new Runnable() {
+ /*
+ * @see java.lang.Runnable#run()
+ */
+ public void run() {
+ BG_COLOR_RGB= display.getSystemColor(SWT.COLOR_INFO_BACKGROUND).getRGB();
+ }
+ });
+ } catch (SWTError err) {
+ // see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=45294
+ if (err.code != SWT.ERROR_DEVICE_DISPOSED)
+ throw err;
+ }
+ }
+ }
+
+ private HTMLPrinter() {
+ }
+
+ private static String replace(String text, char c, String s) {
+
+ int previous= 0;
+ int current= text.indexOf(c, previous);
+
+ if (current == -1)
+ return text;
+
+ StringBuffer buffer= new StringBuffer();
+ while (current > -1) {
+ buffer.append(text.substring(previous, current));
+ buffer.append(s);
+ previous= current + 1;
+ current= text.indexOf(c, previous);
+ }
+ buffer.append(text.substring(previous));
+
+ return buffer.toString();
+ }
+
+ public static String convertToHTMLContent(String content) {
+ content= replace(content, '&', "&amp;"); //$NON-NLS-1$
+ content= replace(content, '"', "&quot;"); //$NON-NLS-1$
+ content= replace(content, '<', "&lt;"); //$NON-NLS-1$
+ return replace(content, '>', "&gt;"); //$NON-NLS-1$
+ }
+
+ public static String read(Reader rd) {
+
+ StringBuffer buffer= new StringBuffer();
+ char[] readBuffer= new char[2048];
+
+ try {
+ int n= rd.read(readBuffer);
+ while (n > 0) {
+ buffer.append(readBuffer, 0, n);
+ n= rd.read(readBuffer);
+ }
+ return buffer.toString();
+ } catch (IOException x) {
+ }
+
+ return null;
+ }
+
+ public static void insertPageProlog(StringBuffer buffer, int position, RGB bgRGB, URL styleSheetURL) {
+
+ if (bgRGB == null)
+ insertPageProlog(buffer, position, styleSheetURL);
+ else {
+ StringBuffer pageProlog= new StringBuffer(300);
+
+ pageProlog.append("<html>"); //$NON-NLS-1$
+
+ appendStyleSheetURL(pageProlog, styleSheetURL);
+
+ pageProlog.append("<body text=\"#000000\" bgcolor=\""); //$NON-NLS-1$
+ appendColor(pageProlog, bgRGB);
+ pageProlog.append("\">"); //$NON-NLS-1$
+
+ buffer.insert(position, pageProlog.toString());
+ }
+ }
+ public static void insertPageProlog(StringBuffer buffer, int position, RGB bgRGB, String styleSheet) {
+
+ if (bgRGB == null)
+ insertPageProlog(buffer, position, styleSheet);
+ else {
+ StringBuffer pageProlog= new StringBuffer(300);
+
+ pageProlog.append("<html>"); //$NON-NLS-1$
+
+ appendStyleSheetURL(pageProlog, styleSheet);
+
+ pageProlog.append("<body text=\"#000000\" bgcolor=\""); //$NON-NLS-1$
+ appendColor(pageProlog, bgRGB);
+ pageProlog.append("\">"); //$NON-NLS-1$
+
+ buffer.insert(position, pageProlog.toString());
+ }
+ }
+
+ public static void insertStyles(StringBuffer buffer, String[] styles) {
+ if (styles == null || styles.length == 0)
+ return;
+
+ StringBuffer styleBuf= new StringBuffer(10 * styles.length);
+ for (int i= 0; styles != null && i < styles.length; i++) {
+ styleBuf.append(" style=\""); //$NON-NLS-1$
+ styleBuf.append(styles[i]);
+ styleBuf.append('"');
+ }
+
+ // Find insertion index
+ // a) within existing body tag with trailing space
+ int index= buffer.indexOf("<body "); //$NON-NLS-1$
+ if (index != -1) {
+ buffer.insert(index+5, styleBuf);
+ return;
+ }
+
+ // b) within existing body tag without attributes
+ index= buffer.indexOf("<body>"); //$NON-NLS-1$
+ if (index != -1) {
+ buffer.insert(index+5, ' ');
+ buffer.insert(index+6, styleBuf);
+ return;
+ }
+ }
+
+ public static void insertPageProlog(StringBuffer buffer, int position, RGB bgRGB) {
+ if (bgRGB == null)
+ insertPageProlog(buffer, position);
+ else {
+ StringBuffer pageProlog= new StringBuffer(60);
+ pageProlog.append("<html><body text=\"#000000\" bgcolor=\""); //$NON-NLS-1$
+ appendColor(pageProlog, bgRGB);
+ pageProlog.append("\">"); //$NON-NLS-1$
+ buffer.insert(position, pageProlog.toString());
+ }
+ }
+
+ private static void appendStyleSheetURL(StringBuffer buffer, String styleSheet) {
+ if (styleSheet == null)
+ return;
+
+ buffer.append("<head><style CHARSET=\"ISO-8859-1\" TYPE=\"text/css\">"); //$NON-NLS-1$
+ buffer.append(styleSheet);
+ buffer.append("</style></head>"); //$NON-NLS-1$
+ }
+
+ private static void appendStyleSheetURL(StringBuffer buffer, URL styleSheetURL) {
+ if (styleSheetURL == null)
+ return;
+
+ buffer.append("<head>"); //$NON-NLS-1$
+
+ buffer.append("<LINK REL=\"stylesheet\" HREF= \""); //$NON-NLS-1$
+ buffer.append(styleSheetURL);
+ buffer.append("\" CHARSET=\"ISO-8859-1\" TYPE=\"text/css\">"); //$NON-NLS-1$
+
+ buffer.append("</head>"); //$NON-NLS-1$
+ }
+
+ private static void appendColor(StringBuffer buffer, RGB rgb) {
+ buffer.append('#');
+ buffer.append(Integer.toHexString(rgb.red));
+ buffer.append(Integer.toHexString(rgb.green));
+ buffer.append(Integer.toHexString(rgb.blue));
+ }
+
+ public static void insertPageProlog(StringBuffer buffer, int position) {
+ insertPageProlog(buffer, position, getBgColor());
+ }
+
+ public static void insertPageProlog(StringBuffer buffer, int position, URL styleSheetURL) {
+ insertPageProlog(buffer, position, getBgColor(), styleSheetURL);
+ }
+
+ public static void insertPageProlog(StringBuffer buffer, int position, String styleSheet) {
+ insertPageProlog(buffer, position, getBgColor(), styleSheet);
+ }
+
+ private static RGB getBgColor() {
+ if (BG_COLOR_RGB != null)
+ return BG_COLOR_RGB;
+ return new RGB(255,255, 225); // RGB value of info bg color on WindowsXP
+
+ }
+
+ public static void addPageProlog(StringBuffer buffer) {
+ insertPageProlog(buffer, buffer.length());
+ }
+
+ public static void addPageEpilog(StringBuffer buffer) {
+ buffer.append("</font></body></html>"); //$NON-NLS-1$
+ }
+
+ public static void startBulletList(StringBuffer buffer) {
+ buffer.append("<ul>"); //$NON-NLS-1$
+ }
+
+ public static void endBulletList(StringBuffer buffer) {
+ buffer.append("</ul>"); //$NON-NLS-1$
+ }
+
+ public static void addBullet(StringBuffer buffer, String bullet) {
+ if (bullet != null) {
+ buffer.append("<li>"); //$NON-NLS-1$
+ buffer.append(bullet);
+ buffer.append("</li>"); //$NON-NLS-1$
+ }
+ }
+
+ public static void addSmallHeader(StringBuffer buffer, String header) {
+ if (header != null) {
+ buffer.append("<h5>"); //$NON-NLS-1$
+ buffer.append(header);
+ buffer.append("</h5>"); //$NON-NLS-1$
+ }
+ }
+
+ public static void addParagraph(StringBuffer buffer, String paragraph) {
+ if (paragraph != null) {
+ buffer.append("<p>"); //$NON-NLS-1$
+ buffer.append(paragraph);
+ }
+ }
+
+ public static void addParagraph(StringBuffer buffer, Reader paragraphReader) {
+ if (paragraphReader != null)
+ addParagraph(buffer, read(paragraphReader));
+ }
+
+ /**
+ * Replaces the following style attributes of the font definition of the <code>html</code>
+ * element:
+ * <ul>
+ * <li>font-size</li>
+ * <li>font-weight</li>
+ * <li>font-style</li>
+ * <li>font-family</li>
+ * </ul>
+ * The font's name is used as font family, a <code>sans-serif</code> default font family is
+ * appened for the case that the given font name is not available.
+ * <p>
+ * If the listed font attributes are not contained in the passed style list, nothing happens.
+ * </p>
+ *
+ * @param styles CSS style definitions
+ * @param fontData the font information to use
+ * @return the modified style definitions
+ * @since 3.3
+ */
+ public static String convertTopLevelFont(String styles, FontData fontData) {
+ boolean bold= (fontData.getStyle() & SWT.BOLD) != 0;
+ boolean italic= (fontData.getStyle() & SWT.ITALIC) != 0;
+ int height= fontData.getHeight();
+ String family= "'" + fontData.getName() + "',sans-serif"; //$NON-NLS-1$ //$NON-NLS-2$
+ styles= styles.replaceFirst("(html\\s*\\{.*(?:\\s|;)font-size:\\s*)\\d+(pt\\;?.*\\})", "$1" + height + "$2"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ styles= styles.replaceFirst("(html\\s*\\{.*(?:\\s|;)font-weight:\\s*)\\w+(\\;?.*\\})", "$1" + (bold ? "bold" : "normal") + "$2"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ styles= styles.replaceFirst("(html\\s*\\{.*(?:\\s|;)font-style:\\s*)\\w+(\\;?.*\\})", "$1" + (italic ? "italic" : "normal") + "$2"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ styles= styles.replaceFirst("(html\\s*\\{.*(?:\\s|;)font-family:\\s*).+?(;.*\\})", "$1" + family + "$2"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ return styles;
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLTextPresenter.java b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLTextPresenter.java
new file mode 100644
index 00000000000..8dc0009c3a1
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/HTMLTextPresenter.java
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.internal.text.html;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Iterator;
+
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Drawable;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.jface.internal.text.link.contentassist.LineBreakingReader;
+
+import org.eclipse.jface.text.DefaultInformationControl;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextPresentation;
+
+
+/**
+ * <p>
+ * Moved into this package from <code>org.eclipse.jface.internal.text.revisions</code>.</p>
+ */
+public class HTMLTextPresenter implements DefaultInformationControl.IInformationPresenter, DefaultInformationControl.IInformationPresenterExtension {
+
+ private static final String LINE_DELIM= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ private int fCounter;
+ private boolean fEnforceUpperLineLimit;
+
+ public HTMLTextPresenter(boolean enforceUpperLineLimit) {
+ super();
+ fEnforceUpperLineLimit= enforceUpperLineLimit;
+ }
+
+ public HTMLTextPresenter() {
+ this(true);
+ }
+
+ protected Reader createReader(String hoverInfo, TextPresentation presentation) {
+ return new HTML2TextReader(new StringReader(hoverInfo), presentation);
+ }
+
+ protected void adaptTextPresentation(TextPresentation presentation, int offset, int insertLength) {
+
+ int yoursStart= offset;
+ int yoursEnd= offset + insertLength -1;
+ yoursEnd= Math.max(yoursStart, yoursEnd);
+
+ Iterator e= presentation.getAllStyleRangeIterator();
+ while (e.hasNext()) {
+
+ StyleRange range= (StyleRange) e.next();
+
+ int myStart= range.start;
+ int myEnd= range.start + range.length -1;
+ myEnd= Math.max(myStart, myEnd);
+
+ if (myEnd < yoursStart)
+ continue;
+
+ if (myStart < yoursStart)
+ range.length += insertLength;
+ else
+ range.start += insertLength;
+ }
+ }
+
+ private void append(StringBuffer buffer, String string, TextPresentation presentation) {
+
+ int length= string.length();
+ buffer.append(string);
+
+ if (presentation != null)
+ adaptTextPresentation(presentation, fCounter, length);
+
+ fCounter += length;
+ }
+
+ private String getIndent(String line) {
+ int length= line.length();
+
+ int i= 0;
+ while (i < length && Character.isWhitespace(line.charAt(i))) ++i;
+
+ return (i == length ? line : line.substring(0, i)) + " "; //$NON-NLS-1$
+ }
+
+ /*
+ * @see IHoverInformationPresenter#updatePresentation(Display display, String, TextPresentation, int, int)
+ */
+ public String updatePresentation(Display display, String hoverInfo, TextPresentation presentation, int maxWidth, int maxHeight) {
+ return updatePresentation((Drawable)display, hoverInfo, presentation, maxWidth, maxHeight);
+ }
+
+ /*
+ * @see IHoverInformationPresenterExtension#updatePresentation(Drawable drawable, String, TextPresentation, int, int)
+ * @since 3.2
+ */
+ public String updatePresentation(Drawable drawable, String hoverInfo, TextPresentation presentation, int maxWidth, int maxHeight) {
+
+ if (hoverInfo == null)
+ return null;
+
+ GC gc= new GC(drawable);
+ try {
+
+ StringBuffer buffer= new StringBuffer();
+ int maxNumberOfLines= Math.round(maxHeight / gc.getFontMetrics().getHeight());
+
+ fCounter= 0;
+ LineBreakingReader reader= new LineBreakingReader(createReader(hoverInfo, presentation), gc, maxWidth);
+
+ boolean lastLineFormatted= false;
+ String lastLineIndent= null;
+
+ String line=reader.readLine();
+ boolean lineFormatted= reader.isFormattedLine();
+ boolean firstLineProcessed= false;
+
+ while (line != null) {
+
+ if (fEnforceUpperLineLimit && maxNumberOfLines <= 0)
+ break;
+
+ if (firstLineProcessed) {
+ if (!lastLineFormatted)
+ append(buffer, LINE_DELIM, null);
+ else {
+ append(buffer, LINE_DELIM, presentation);
+ if (lastLineIndent != null)
+ append(buffer, lastLineIndent, presentation);
+ }
+ }
+
+ append(buffer, line, null);
+ firstLineProcessed= true;
+
+ lastLineFormatted= lineFormatted;
+ if (!lineFormatted)
+ lastLineIndent= null;
+ else if (lastLineIndent == null)
+ lastLineIndent= getIndent(line);
+
+ line= reader.readLine();
+ lineFormatted= reader.isFormattedLine();
+
+ maxNumberOfLines--;
+ }
+
+ if (line != null) {
+ append(buffer, LINE_DELIM, lineFormatted ? presentation : null);
+ append(buffer, HTMLMessages.getString("HTMLTextPresenter.ellipse"), presentation); //$NON-NLS-1$
+ }
+
+ return trim(buffer, presentation);
+
+ } catch (IOException e) {
+
+ // ignore TODO do something else?
+ return null;
+
+ } finally {
+ gc.dispose();
+ }
+ }
+
+ private String trim(StringBuffer buffer, TextPresentation presentation) {
+
+ int length= buffer.length();
+
+ int end= length -1;
+ while (end >= 0 && Character.isWhitespace(buffer.charAt(end)))
+ -- end;
+
+ if (end == -1)
+ return ""; //$NON-NLS-1$
+
+ if (end < length -1)
+ buffer.delete(end + 1, length);
+ else
+ end= length;
+
+ int start= 0;
+ while (start < end && Character.isWhitespace(buffer.charAt(start)))
+ ++ start;
+
+ buffer.delete(0, start);
+ presentation.setResultWindow(new Region(start, buffer.length()));
+ return buffer.toString();
+ }
+}
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/SingleCharReader.java b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/SingleCharReader.java
new file mode 100644
index 00000000000..fd8e33d576e
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/SingleCharReader.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.internal.text.html;
+
+import java.io.IOException;
+import java.io.Reader;
+
+
+/**
+ * <p>
+ * Moved into this package from <code>org.eclipse.jface.internal.text.revisions</code>.</p>
+ */
+public abstract class SingleCharReader extends Reader {
+
+ /**
+ * @see Reader#read()
+ */
+ public abstract int read() throws IOException;
+
+ /**
+ * @see Reader#read(char[],int,int)
+ */
+ public int read(char cbuf[], int off, int len) throws IOException {
+ int end= off + len;
+ for (int i= off; i < end; i++) {
+ int ch= read();
+ if (ch == -1) {
+ if (i == off)
+ return -1;
+ return i - off;
+ }
+ cbuf[i]= (char)ch;
+ }
+ return len;
+ }
+
+ /**
+ * @see Reader#ready()
+ */
+ public boolean ready() throws IOException {
+ return true;
+ }
+
+ /**
+ * Returns the readable content as string.
+ * @return the readable content as string
+ * @exception IOException in case reading fails
+ */
+ public String getString() throws IOException {
+ StringBuffer buf= new StringBuffer();
+ int ch;
+ while ((ch= read()) != -1) {
+ buf.append((char)ch);
+ }
+ return buf.toString();
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/SubstitutionTextReader.java b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/SubstitutionTextReader.java
new file mode 100644
index 00000000000..23d3309db39
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/SubstitutionTextReader.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.internal.text.html;
+
+import java.io.IOException;
+import java.io.Reader;
+
+
+/**
+ * Reads the text contents from a reader and computes for each character
+ * a potential substitution. The substitution may eat more characters than
+ * only the one passed into the computation routine.
+ * <p>
+ * Moved into this package from <code>org.eclipse.jface.internal.text.revisions</code>.</p>
+ */
+public abstract class SubstitutionTextReader extends SingleCharReader {
+
+ protected static final String LINE_DELIM= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ private Reader fReader;
+ protected boolean fWasWhiteSpace;
+ private int fCharAfterWhiteSpace;
+
+ /**
+ * Tells whether white space characters are skipped.
+ */
+ private boolean fSkipWhiteSpace= true;
+
+ private boolean fReadFromBuffer;
+ private StringBuffer fBuffer;
+ private int fIndex;
+
+
+ protected SubstitutionTextReader(Reader reader) {
+ fReader= reader;
+ fBuffer= new StringBuffer();
+ fIndex= 0;
+ fReadFromBuffer= false;
+ fCharAfterWhiteSpace= -1;
+ fWasWhiteSpace= true;
+ }
+
+ /**
+ * Computes the substitution for the given character and if necessary
+ * subsequent characters. Implementation should use <code>nextChar</code>
+ * to read subsequent characters.
+ *
+ * @param c the character to be substituted
+ * @return the substitution for <code>c</code>
+ * @throws IOException in case computing the substitution fails
+ */
+ protected abstract String computeSubstitution(int c) throws IOException;
+
+ /**
+ * Returns the internal reader.
+ *
+ * @return the internal reader
+ */
+ protected Reader getReader() {
+ return fReader;
+ }
+
+ /**
+ * Returns the next character.
+ * @return the next character
+ * @throws IOException in case reading the character fails
+ */
+ protected int nextChar() throws IOException {
+ fReadFromBuffer= (fBuffer.length() > 0);
+ if (fReadFromBuffer) {
+ char ch= fBuffer.charAt(fIndex++);
+ if (fIndex >= fBuffer.length()) {
+ fBuffer.setLength(0);
+ fIndex= 0;
+ }
+ return ch;
+ }
+
+ int ch= fCharAfterWhiteSpace;
+ if (ch == -1) {
+ ch= fReader.read();
+ }
+ if (fSkipWhiteSpace && Character.isWhitespace((char)ch)) {
+ do {
+ ch= fReader.read();
+ } while (Character.isWhitespace((char)ch));
+ if (ch != -1) {
+ fCharAfterWhiteSpace= ch;
+ return ' ';
+ }
+ } else {
+ fCharAfterWhiteSpace= -1;
+ }
+ return ch;
+ }
+
+ /**
+ * @see Reader#read()
+ */
+ public int read() throws IOException {
+ int c;
+ do {
+
+ c= nextChar();
+ while (!fReadFromBuffer) {
+ String s= computeSubstitution(c);
+ if (s == null)
+ break;
+ if (s.length() > 0)
+ fBuffer.insert(0, s);
+ c= nextChar();
+ }
+
+ } while (fSkipWhiteSpace && fWasWhiteSpace && (c == ' '));
+ fWasWhiteSpace= (c == ' ' || c == '\r' || c == '\n');
+ return c;
+ }
+
+ /**
+ * @see Reader#ready()
+ */
+ public boolean ready() throws IOException {
+ return fReader.ready();
+ }
+
+ /**
+ * @see Reader#close()
+ */
+ public void close() throws IOException {
+ fReader.close();
+ }
+
+ /**
+ * @see Reader#reset()
+ */
+ public void reset() throws IOException {
+ fReader.reset();
+ fWasWhiteSpace= true;
+ fCharAfterWhiteSpace= -1;
+ fBuffer.setLength(0);
+ fIndex= 0;
+ }
+
+ protected final void setSkipWhitespace(boolean state) {
+ fSkipWhiteSpace= state;
+ }
+
+ protected final boolean isSkippingWhitespace() {
+ return fSkipWhiteSpace;
+ }
+}

Back to the top