diff options
author | Dani Megert | 2006-09-22 15:17:39 +0000 |
---|---|---|
committer | Dani Megert | 2006-09-22 15:17:39 +0000 |
commit | b3b4d7bbb5e53949ead3ed5da6bdf902a9d4ebbb (patch) | |
tree | 86645f6b1bf72a921e686db1a84b5a7ea9cf69bb /org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html | |
parent | 1932e846bc3e2eaa301b5ec4094d14c42103a281 (diff) | |
download | eclipse.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')
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, '&', "&"); //$NON-NLS-1$ + content= replace(content, '"', """); //$NON-NLS-1$ + content= replace(content, '<', "<"); //$NON-NLS-1$ + return replace(content, '>', ">"); //$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; + } +} |