DocumentationHover improvements
diff --git a/core/plugins/org.eclipse.dltk.ui/DocumentationHoverStyleSheet.css b/core/plugins/org.eclipse.dltk.ui/DocumentationHoverStyleSheet.css
index dcf2813..fafa4b9 100644
--- a/core/plugins/org.eclipse.dltk.ui/DocumentationHoverStyleSheet.css
+++ b/core/plugins/org.eclipse.dltk.ui/DocumentationHoverStyleSheet.css
@@ -12,10 +12,10 @@
 h5           { margin-top: 0px; margin-bottom: 0px; }
 p            { margin-top: 1em; margin-bottom: 1em; }
 pre	         { margin-left: 0.6em; }
-ul	         { margin-top: 0px; margin-bottom: 1em; }
+ul	         { margin-top: 0px; margin-bottom: 1em; margin-left: 1em; padding-left: 1em;}
 li	         { margin-top: 0px; margin-bottom: 0px; } 
 li p	     { margin-top: 0px; margin-bottom: 0px; } 
-ol	         { margin-top: 0px; margin-bottom: 1em; }
+ol	         { margin-top: 0px; margin-bottom: 1em; margin-left: 1em; padding-left: 1em; }
 dl	         { margin-top: 0px; margin-bottom: 1em; }
 dt	         { margin-top: 0px; margin-bottom: 0px; font-weight: bold; }
 dd	         { margin-top: 0px; margin-bottom: 0px; }
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/BrowserInformationControl2.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/BrowserInformationControl2.java
index 7a5ccb1..aa88309 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/BrowserInformationControl2.java
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/BrowserInformationControl2.java
@@ -8,6 +8,9 @@
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Shell;
 
+/*
+ * TODO (alex) to be removed 
+ */
 public class BrowserInformationControl2 extends BrowserInformationControl
 		implements IInformationControlExtension4, IInformationControlExtension5 {
 	public BrowserInformationControl2(Shell parent, int shellStyle, int style,
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/HTMLPrinter.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/HTMLPrinter.java
index ddf1b20..4fc9d1c 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/HTMLPrinter.java
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/HTMLPrinter.java
@@ -197,7 +197,7 @@
 	public static void addSmallHeader(StringBuffer buffer, String header) {
 		if (header != null) {
 			buffer.append("<h5>"); //$NON-NLS-1$
-			buffer.append(TextUtils.escapeHTML(header));
+			buffer.append(header);
 			buffer.append("</h5>"); //$NON-NLS-1$
 		}
 	}
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/CompletionHoverControlCreator.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/CompletionHoverControlCreator.java
index fa8e32c..d7efe98 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/CompletionHoverControlCreator.java
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/CompletionHoverControlCreator.java
@@ -15,6 +15,8 @@
 /**
  * Hover control creator.
  * 
+ * TODO (alex) to be removed
+ * 
  * @since 2.0
  */
 public final class CompletionHoverControlCreator extends
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/DocumentationBrowserInformationControlInput.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/DocumentationBrowserInformationControlInput.java
new file mode 100644
index 0000000..a1df81b
--- /dev/null
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/DocumentationBrowserInformationControlInput.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 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.dltk.internal.ui.text.hover;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.dltk.core.IModelElement;
+import org.eclipse.jface.internal.text.html.BrowserInformationControlInput;
+
+/**
+ * Browser input for Javadoc hover.
+ * 
+ * @since 4.0
+ */
+@SuppressWarnings("restriction")
+public class DocumentationBrowserInformationControlInput extends
+		BrowserInformationControlInput {
+
+	private final Object fElement;
+	private final String fHtml;
+	private final int fLeadingImageWidth;
+
+	/**
+	 * Creates a new browser information control input.
+	 * 
+	 * @param previous
+	 *            previous input, or <code>null</code> if none available
+	 * @param element
+	 *            the element, or <code>null</code> if none available
+	 * @param html
+	 *            HTML contents, must not be null
+	 * @param leadingImageWidth
+	 *            the indent required for the element image
+	 */
+	public DocumentationBrowserInformationControlInput(
+			DocumentationBrowserInformationControlInput previous,
+			Object element, String html, int leadingImageWidth) {
+		super(previous);
+		Assert.isNotNull(html);
+		fElement = element;
+		fHtml = html;
+		fLeadingImageWidth = leadingImageWidth;
+	}
+
+	/*
+	 * @see BrowserInformationControlInput#getLeadingImageWidth()
+	 * 
+	 * @since 4.0
+	 */
+	@Override
+	public int getLeadingImageWidth() {
+		return fLeadingImageWidth;
+	}
+
+	/**
+	 * Returns the Java element.
+	 * 
+	 * @return the element or <code>null</code> if none available
+	 */
+	public Object getElement() {
+		return fElement;
+	}
+
+	/*
+	 * @see org.eclipse.jface.internal.text.html.BrowserInput#getHtml()
+	 */
+	@Override
+	public String getHtml() {
+		return fHtml;
+	}
+
+	/*
+	 * @see org.eclipse.jdt.internal.ui.infoviews.BrowserInput#getInputElement()
+	 */
+	@Override
+	public Object getInputElement() {
+		return fElement == null ? (Object) fHtml : fElement;
+	}
+
+	/*
+	 * @see org.eclipse.jdt.internal.ui.infoviews.BrowserInput#getInputName()
+	 */
+	@Override
+	public String getInputName() {
+		return fElement instanceof IModelElement ? ((IModelElement) fElement)
+				.getElementName() : ""; //$NON-NLS-1$
+	}
+
+}
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/DocumentationHover.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/DocumentationHover.java
index 113bbfd..c2604f6 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/DocumentationHover.java
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/DocumentationHover.java
@@ -11,37 +11,52 @@
 
 import java.io.IOException;
 import java.io.Reader;
+import java.net.URL;
 
 import org.eclipse.dltk.core.IModelElement;
 import org.eclipse.dltk.core.ModelException;
-import org.eclipse.dltk.internal.ui.BrowserInformationControl;
 import org.eclipse.dltk.internal.ui.text.HTMLPrinter;
-import org.eclipse.dltk.internal.ui.text.HTMLTextPresenter;
 import org.eclipse.dltk.internal.ui.text.IInformationControlExtension4;
+import org.eclipse.dltk.ui.DLTKPluginImages;
+import org.eclipse.dltk.ui.DLTKUIPlugin;
+import org.eclipse.dltk.ui.PreferenceConstants;
+import org.eclipse.dltk.ui.ScriptElementImageProvider;
 import org.eclipse.dltk.ui.ScriptElementLabels;
 import org.eclipse.dltk.ui.documentation.IDocumentationResponse;
 import org.eclipse.dltk.ui.documentation.IScriptDocumentationTitleAdapter;
 import org.eclipse.dltk.ui.documentation.ScriptDocumentationAccess;
 import org.eclipse.dltk.ui.documentation.TextDocumentationResponse;
-import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.internal.text.html.BrowserInformationControl;
+import org.eclipse.jface.internal.text.html.BrowserInformationControlInput;
+import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
 import org.eclipse.jface.text.DefaultInformationControl;
 import org.eclipse.jface.text.IInformationControl;
 import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IInformationControlExtension5;
+import org.eclipse.jface.text.IInputChangedListener;
 import org.eclipse.jface.text.ITextHoverExtension;
 import org.eclipse.jface.text.information.IInformationProviderExtension2;
+import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchSite;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.editors.text.EditorsUI;
 
 /**
  * Provides documentation as hover info for Script elements and keywords.
- * 
- * 
  */
+@SuppressWarnings("restriction")
 public class DocumentationHover extends AbstractScriptEditorTextHover implements
 		IInformationProviderExtension2, ITextHoverExtension {
 
-	private final long LABEL_FLAGS = // ScriptElementLabels.ALL_FULLY_QUALIFIED
+	private static final long LABEL_FLAGS = // ScriptElementLabels.ALL_FULLY_QUALIFIED
 	ScriptElementLabels.M_APP_RETURNTYPE
 			| ScriptElementLabels.F_APP_TYPE_SIGNATURE
 			| ScriptElementLabels.M_PARAMETER_TYPES
@@ -51,69 +66,365 @@
 			| ScriptElementLabels.M_PRE_TYPE_PARAMETERS
 			| ScriptElementLabels.T_TYPE_PARAMETERS
 			| ScriptElementLabels.USE_RESOLVED;
-	private final long LOCAL_VARIABLE_FLAGS = LABEL_FLAGS
+	private static final long LOCAL_VARIABLE_FLAGS = LABEL_FLAGS
 			& ~ScriptElementLabels.F_FULLY_QUALIFIED
 			| ScriptElementLabels.F_POST_QUALIFIED;
 
 	/**
 	 * The hover control creator.
-	 * 
-	 * 
 	 */
 	private IInformationControlCreator fHoverControlCreator;
+
 	/**
 	 * The presentation control creator.
-	 * 
-	 * 
 	 */
 	private IInformationControlCreator fPresenterControlCreator;
 
+	/**
+	 * Action to go back to the previous input in the hover control.
+	 * 
+	 * @since 4.0
+	 */
+	private static final class BackAction extends Action {
+		private final BrowserInformationControl fInfoControl;
+
+		public BackAction(BrowserInformationControl infoControl) {
+			fInfoControl = infoControl;
+			setText(ScriptHoverMessages.ScriptdocHover_back);
+			ISharedImages images = PlatformUI.getWorkbench().getSharedImages();
+			setImageDescriptor(images
+					.getImageDescriptor(ISharedImages.IMG_TOOL_BACK));
+			setDisabledImageDescriptor(images
+					.getImageDescriptor(ISharedImages.IMG_TOOL_BACK_DISABLED));
+
+			update();
+		}
+
+		@Override
+		public void run() {
+			BrowserInformationControlInput previous = (BrowserInformationControlInput) fInfoControl
+					.getInput().getPrevious();
+			if (previous != null) {
+				fInfoControl.setInput(previous);
+			}
+		}
+
+		public void update() {
+			BrowserInformationControlInput current = fInfoControl.getInput();
+
+			if (current != null && current.getPrevious() != null) {
+				// BrowserInput previous = current.getPrevious();
+				// setToolTipText(Messages.format(
+				// JavaHoverMessages.JavadocHover_back_toElement_toolTip,
+				// BasicElementLabels.getJavaElementName(previous
+				// .getInputName())));
+				setEnabled(true);
+			} else {
+				setEnabled(false);
+			}
+			setToolTipText(ScriptHoverMessages.ScriptdocHover_back);
+		}
+	}
+
+	/**
+	 * Action to go forward to the next input in the hover control.
+	 * 
+	 * @since 4.0
+	 */
+	private static final class ForwardAction extends Action {
+		private final BrowserInformationControl fInfoControl;
+
+		public ForwardAction(BrowserInformationControl infoControl) {
+			fInfoControl = infoControl;
+			setText(ScriptHoverMessages.ScriptdocHover_forward);
+			ISharedImages images = PlatformUI.getWorkbench().getSharedImages();
+			setImageDescriptor(images
+					.getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD));
+			setDisabledImageDescriptor(images
+					.getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD_DISABLED));
+
+			update();
+		}
+
+		@Override
+		public void run() {
+			BrowserInformationControlInput next = (BrowserInformationControlInput) fInfoControl
+					.getInput().getNext();
+			if (next != null) {
+				fInfoControl.setInput(next);
+			}
+		}
+
+		public void update() {
+			BrowserInformationControlInput current = fInfoControl.getInput();
+
+			if (current != null && current.getNext() != null) {
+				// setToolTipText(Messages
+				// .format(JavaHoverMessages.JavadocHover_forward_toElement_toolTip,
+				// BasicElementLabels.getJavaElementName(current
+				// .getNext().getInputName())));
+				setEnabled(true);
+			} else {
+				setEnabled(false);
+			}
+			setToolTipText(ScriptHoverMessages.ScriptdocHover_forward);
+		}
+	}
+
+	/**
+	 * Action that opens the current hover input element.
+	 * 
+	 * @since 4.0
+	 */
+	private static final class OpenDeclarationAction extends Action {
+		private final BrowserInformationControl fInfoControl;
+
+		public OpenDeclarationAction(BrowserInformationControl infoControl) {
+			fInfoControl = infoControl;
+			setText(ScriptHoverMessages.ScriptdocHover_openDeclaration);
+			DLTKPluginImages.setLocalImageDescriptors(this, "goto_input.gif"); //$NON-NLS-1$ //TODO: better images
+		}
+
+		/*
+		 * @see org.eclipse.jface.action.Action#run()
+		 */
+		@Override
+		public void run() {
+			DocumentationBrowserInformationControlInput infoInput = (DocumentationBrowserInformationControlInput) fInfoControl
+					.getInput(); // TODO: check cast
+			fInfoControl.notifyDelayedInputChange(null);
+			fInfoControl.dispose(); // FIXME: should have protocol to hide,
+									// rather than dispose
+
+			try {
+				if (infoInput.getElement() instanceof IModelElement) {
+					// FIXME: add hover location to editor navigation history?
+					DLTKUIPlugin.openInEditor((IModelElement) infoInput
+							.getElement());
+				}
+				// TODO (alex) try via IOpenDelegate
+			} catch (PartInitException e) {
+				DLTKUIPlugin.log(e);
+			} catch (ModelException e) {
+				DLTKUIPlugin.log(e);
+			}
+		}
+	}
+
+	/**
+	 * Presenter control creator.
+	 * 
+	 * @since 4.0
+	 */
+	public static final class PresenterControlCreator extends
+			AbstractReusableInformationControlCreator {
+
+		private IWorkbenchSite fSite;
+
+		/**
+		 * Creates a new PresenterControlCreator.
+		 * 
+		 * @param site
+		 *            the site or <code>null</code> if none
+		 */
+		public PresenterControlCreator(IWorkbenchSite site) {
+			fSite = site;
+		}
+
+		/*
+		 * @see org.eclipse.jdt.internal.ui.text.java.hover.
+		 * AbstractReusableInformationControlCreator
+		 * #doCreateInformationControl(org.eclipse.swt.widgets.Shell)
+		 */
+		@Override
+		public IInformationControl doCreateInformationControl(Shell parent) {
+			if (BrowserInformationControl.isAvailable(parent)) {
+				ToolBarManager tbm = new ToolBarManager(SWT.FLAT);
+				String font = PreferenceConstants.APPEARANCE_DOCUMENTATION_FONT;
+				BrowserInformationControl iControl = new BrowserInformationControl(
+						parent, font, tbm);
+
+				final BackAction backAction = new BackAction(iControl);
+				backAction.setEnabled(false);
+				tbm.add(backAction);
+				final ForwardAction forwardAction = new ForwardAction(iControl);
+				tbm.add(forwardAction);
+				forwardAction.setEnabled(false);
+
+				// final ShowInJavadocViewAction showInJavadocViewAction = new
+				// ShowInJavadocViewAction(
+				// iControl);
+				// tbm.add(showInJavadocViewAction);
+				final OpenDeclarationAction openDeclarationAction = new OpenDeclarationAction(
+						iControl);
+				tbm.add(openDeclarationAction);
+
+				// final SimpleSelectionProvider selectionProvider = new
+				// SimpleSelectionProvider();
+				// if (fSite != null) {
+				// OpenAttachedJavadocAction openAttachedJavadocAction = new
+				// OpenAttachedJavadocAction(
+				// fSite);
+				// openAttachedJavadocAction
+				// .setSpecialSelectionProvider(selectionProvider);
+				// openAttachedJavadocAction
+				// .setImageDescriptor(DLTKPluginImages.DESC_ELCL_OPEN_BROWSER);
+				// openAttachedJavadocAction
+				// .setDisabledImageDescriptor(DLTKPluginImages.DESC_DLCL_OPEN_BROWSER);
+				// selectionProvider
+				// .addSelectionChangedListener(openAttachedJavadocAction);
+				// selectionProvider.setSelection(new StructuredSelection());
+				// tbm.add(openAttachedJavadocAction);
+				// }
+
+				IInputChangedListener inputChangeListener = new IInputChangedListener() {
+					public void inputChanged(Object newInput) {
+						backAction.update();
+						forwardAction.update();
+						if (newInput == null) {
+							// selectionProvider
+							// .setSelection(new StructuredSelection());
+						} else if (newInput instanceof BrowserInformationControlInput) {
+							BrowserInformationControlInput input = (BrowserInformationControlInput) newInput;
+							Object inputElement = input.getInputElement();
+							// selectionProvider
+							// .setSelection(new StructuredSelection(
+							// inputElement));
+							boolean isJavaElementInput = inputElement instanceof IModelElement;
+							// showInJavadocViewAction
+							// .setEnabled(isJavaElementInput);
+							openDeclarationAction
+									.setEnabled(isJavaElementInput);
+						}
+					}
+				};
+				iControl.addInputChangeListener(inputChangeListener);
+
+				tbm.update(true);
+
+				// TODO (alex) addLinkListener(iControl);
+				return iControl;
+
+			} else {
+				return new DefaultInformationControl(parent, true);
+			}
+		}
+	}
+
 	public IInformationControlCreator getInformationPresenterControlCreator() {
 		if (fPresenterControlCreator == null) {
-			fPresenterControlCreator = new AbstractReusableInformationControlCreator() {
-				public IInformationControl doCreateInformationControl(
-						Shell parent) {
-					if (BrowserInformationControl.isAvailable(parent))
-						return new BrowserInformationControl(parent,
-								JFaceResources.DIALOG_FONT, true);
-					else
-						return new DefaultInformationControl(parent,
-								new HTMLTextPresenter(false));
-				}
-			};
+			fPresenterControlCreator = new PresenterControlCreator(getSite());
 		}
 		return fPresenterControlCreator;
 	}
 
+	private IWorkbenchSite getSite() {
+		IEditorPart editor = getEditor();
+		if (editor == null) {
+			IWorkbenchPage page = DLTKUIPlugin.getActivePage();
+			if (page != null)
+				editor = page.getActiveEditor();
+		}
+		if (editor != null)
+			return editor.getSite();
+
+		return null;
+	}
+
+	/**
+	 * Hover control creator.
+	 * 
+	 * @since 4.0
+	 */
+	public static final class HoverControlCreator extends
+			AbstractReusableInformationControlCreator {
+		/**
+		 * The information presenter control creator.
+		 */
+		private final IInformationControlCreator fInformationPresenterControlCreator;
+
+		/**
+		 * <code>true</code> to use the additional info affordance,
+		 * <code>false</code> to use the hover affordance.
+		 */
+		private final boolean fAdditionalInfoAffordance;
+
+		/**
+		 * @param informationPresenterControlCreator
+		 *            control creator for enriched hover
+		 */
+		public HoverControlCreator(
+				IInformationControlCreator informationPresenterControlCreator) {
+			this(informationPresenterControlCreator, false);
+		}
+
+		/**
+		 * @param informationPresenterControlCreator
+		 *            control creator for enriched hover
+		 * @param additionalInfoAffordance
+		 *            <code>true</code> to use the additional info affordance,
+		 *            <code>false</code> to use the hover affordance
+		 */
+		public HoverControlCreator(
+				IInformationControlCreator informationPresenterControlCreator,
+				boolean additionalInfoAffordance) {
+			fInformationPresenterControlCreator = informationPresenterControlCreator;
+			fAdditionalInfoAffordance = additionalInfoAffordance;
+		}
+
+		/**
+		 * @see AbstractReusableInformationControlCreator#doCreateInformationControl(Shell)
+		 */
+		@Override
+		public IInformationControl doCreateInformationControl(Shell parent) {
+			String tooltipAffordanceString = fAdditionalInfoAffordance ? DLTKUIPlugin
+					.getAdditionalInfoAffordanceString() : EditorsUI
+					.getTooltipAffordanceString();
+			if (BrowserInformationControl.isAvailable(parent)) {
+				String font = PreferenceConstants.APPEARANCE_DOCUMENTATION_FONT;
+				BrowserInformationControl iControl = new BrowserInformationControl(
+						parent, font, tooltipAffordanceString) {
+					/**
+					 * @see IInformationControlExtension5#getInformationPresenterControlCreator()
+					 */
+					@Override
+					public IInformationControlCreator getInformationPresenterControlCreator() {
+						return fInformationPresenterControlCreator;
+					}
+				};
+				// TODO (alex) addLinkListener(iControl);
+				return iControl;
+			} else {
+				return new DefaultInformationControl(parent,
+						tooltipAffordanceString);
+			}
+		}
+
+		/**
+		 * @see AbstractReusableInformationControlCreator#canReuse(IInformationControl)
+		 */
+		@Override
+		public boolean canReuse(IInformationControl control) {
+			if (!super.canReuse(control))
+				return false;
+
+			if (control instanceof IInformationControlExtension4) {
+				String tooltipAffordanceString = fAdditionalInfoAffordance ? DLTKUIPlugin
+						.getAdditionalInfoAffordanceString() : EditorsUI
+						.getTooltipAffordanceString();
+				((IInformationControlExtension4) control)
+						.setStatusText(tooltipAffordanceString);
+			}
+
+			return true;
+		}
+	}
+
 	@Override
 	public IInformationControlCreator getHoverControlCreator() {
-		if (fHoverControlCreator == null) {
-			fHoverControlCreator = new AbstractReusableInformationControlCreator() {
-				public IInformationControl doCreateInformationControl(
-						Shell parent) {
-					if (BrowserInformationControl.isAvailable(parent))
-						return new BrowserInformationControl(parent,
-								JFaceResources.DIALOG_FONT,
-								EditorsUI
-								.getTooltipAffordanceString());
-					else
-						return new DefaultInformationControl(parent,
-								EditorsUI.getTooltipAffordanceString(),
-								new HTMLTextPresenter(true));
-				}
-
-				public boolean canReuse(IInformationControl control) {
-					boolean canReuse = super.canReuse(control);
-					if (canReuse
-							&& control instanceof IInformationControlExtension4)
-						((IInformationControlExtension4) control)
-								.setStatusText(EditorsUI
-										.getTooltipAffordanceString());
-					return canReuse;
-
-				}
-			};
-		}
+		if (fHoverControlCreator == null)
+			fHoverControlCreator = new HoverControlCreator(
+					getInformationPresenterControlCreator());
 		return fHoverControlCreator;
 	}
 
@@ -126,8 +437,10 @@
 
 		boolean hasContents = false;
 		if (nResults > 1) {
-			HTMLPrinter
-					.addSmallHeader(buffer, titleAdapter.getTitle(result[0]));
+			HTMLPrinter.addSmallHeader(
+					buffer,
+					getInfoText(result[0], titleAdapter.getTitle(result[0]),
+							titleAdapter.getImage(result[0])));
 			HTMLPrinter.addParagraph(buffer, "<hr>"); //$NON-NLS-1$
 			for (int i = 0; i < result.length; i++) {
 				Object element = result[i];
@@ -151,10 +464,14 @@
 				response = new TextDocumentationResponse(
 						element,
 						titleAdapter.getTitle(element),
+						titleAdapter.getImage(element),
 						ScriptHoverMessages.ScriptdocHover_noAttachedInformation);
 			}
 			try {
-				HTMLPrinter.addSmallHeader(buffer, response.getTitle());
+				HTMLPrinter.addSmallHeader(
+						buffer,
+						getInfoText(element, response.getTitle(),
+								response.getImage()));
 				HTMLPrinter.addParagraph(buffer, response.getReader());
 				hasContents = true;
 			} catch (IOException e) {
@@ -177,6 +494,71 @@
 		return null;
 	}
 
+	private String getInfoText(Object element, String title,
+			ImageDescriptor image) {
+		String imageName = null;
+		if (image != null) {
+			final URL imageURL = DLTKUIPlugin.getDefault()
+					.getImagesOnFSRegistry().getImageURL(image);
+			if (imageURL != null) {
+				imageName = imageURL.toExternalForm();
+			}
+		}
+		StringBuffer buf = new StringBuffer();
+		addImageAndLabel(buf, element, imageName, 16, 16, title, 20, 2);
+		return buf.toString();
+	}
+
+	private static void addImageAndLabel(StringBuffer buf, Object element,
+			String imageSrcPath, int imageWidth, int imageHeight, String label,
+			int labelLeft, int labelTop) {
+		buf.append("<div style='word-wrap: break-word; position: relative; "); //$NON-NLS-1$
+
+		if (imageSrcPath != null) {
+			buf.append("margin-left: ").append(labelLeft).append("px; "); //$NON-NLS-1$ //$NON-NLS-2$
+			buf.append("padding-top: ").append(labelTop).append("px; "); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+
+		buf.append("'>"); //$NON-NLS-1$
+		if (imageSrcPath != null) {
+			// if (element != null) {
+			// try {
+			// String uri = JavaElementLinks.createURI(
+			// JavaElementLinks.OPEN_LINK_SCHEME, element);
+			//					buf.append("<a href='").append(uri).append("'>"); //$NON-NLS-1$//$NON-NLS-2$
+			// } catch (URISyntaxException e) {
+			// element = null; // no link
+			// }
+			// }
+			StringBuffer imageStyle = new StringBuffer(
+					"border:none; position: absolute; "); //$NON-NLS-1$
+			imageStyle.append("width: ").append(imageWidth).append("px; "); //$NON-NLS-1$ //$NON-NLS-2$
+			imageStyle.append("height: ").append(imageHeight).append("px; "); //$NON-NLS-1$ //$NON-NLS-2$
+			imageStyle.append("left: ").append(-labelLeft - 1).append("px; "); //$NON-NLS-1$ //$NON-NLS-2$
+
+			// hack for broken transparent PNG support in IE 6, see
+			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=223900 :
+			buf.append("<!--[if lte IE 6]><![if gte IE 5.5]>\n"); //$NON-NLS-1$
+			String tooltip = element == null ? "" : "alt='" + ScriptHoverMessages.ScriptdocHover_openDeclaration + "' "; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			buf.append("<span ").append(tooltip).append("style=\"").append(imageStyle). //$NON-NLS-1$ //$NON-NLS-2$
+					append("filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='").append(imageSrcPath).append("')\"></span>\n"); //$NON-NLS-1$ //$NON-NLS-2$
+			buf.append("<![endif]><![endif]-->\n"); //$NON-NLS-1$
+
+			buf.append("<!--[if !IE]>-->\n"); //$NON-NLS-1$
+			buf.append("<img ").append(tooltip).append("style='").append(imageStyle).append("' src='").append(imageSrcPath).append("'/>\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+			buf.append("<!--<![endif]-->\n"); //$NON-NLS-1$
+			buf.append("<!--[if gte IE 7]>\n"); //$NON-NLS-1$
+			buf.append("<img ").append(tooltip).append("style='").append(imageStyle).append("' src='").append(imageSrcPath).append("'/>\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+			buf.append("<![endif]-->\n"); //$NON-NLS-1$
+			// if (element != null) {
+			//				buf.append("</a>"); //$NON-NLS-1$
+			// }
+		}
+
+		buf.append(label);
+		buf.append("</div>"); //$NON-NLS-1$
+	}
+
 	@Override
 	protected String getHoverInfo(String nature, String content) {
 		try {
@@ -197,7 +579,10 @@
 		return null;
 	}
 
-	private final IScriptDocumentationTitleAdapter titleAdapter = new IScriptDocumentationTitleAdapter() {
+	private static final IScriptDocumentationTitleAdapter titleAdapter = new IScriptDocumentationTitleAdapter() {
+
+		private ScriptElementImageProvider fImageProvider = new ScriptElementImageProvider();
+
 		public String getTitle(Object element) {
 			if (element instanceof IModelElement) {
 				IModelElement member = (IModelElement) element;
@@ -210,5 +595,18 @@
 				return null;
 			}
 		}
+
+		public ImageDescriptor getImage(Object element) {
+			if (element instanceof IModelElement) {
+				final IModelElement modelElement = (IModelElement) element;
+				if (fImageProvider == null) {
+					fImageProvider = new ScriptElementImageProvider();
+				}
+				return fImageProvider.getScriptImageDescriptor(modelElement,
+						ScriptElementImageProvider.OVERLAY_ICONS
+								| ScriptElementImageProvider.SMALL_ICONS);
+			}
+			return null;
+		}
 	};
 }
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/ScriptHoverMessages.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/ScriptHoverMessages.java
index c6ea258..8d41747 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/ScriptHoverMessages.java
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/ScriptHoverMessages.java
@@ -16,12 +16,18 @@
  */
 public final class ScriptHoverMessages extends NLS {
 
-	private static final String BUNDLE_NAME= ScriptHoverMessages.class.getName();
+	private static final String BUNDLE_NAME = ScriptHoverMessages.class
+			.getName();
 
 	private ScriptHoverMessages() {
 		// Do not instantiate
 	}
 
+	public static String ScriptdocHover_back;
+	public static String ScriptdocHover_forward;
+
+	public static String ScriptdocHover_openDeclaration;
+
 	public static String ScriptdocHover_noAttachedInformation;
 	public static String ScriptTextHover_makeStickyHint;
 	public static String NoBreakpointAnnotation_addBreakpoint;
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/ScriptHoverMessages.properties b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/ScriptHoverMessages.properties
index 721c900..0e86ecf 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/ScriptHoverMessages.properties
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/internal/ui/text/hover/ScriptHoverMessages.properties
@@ -8,9 +8,11 @@
 
 ###############################################################################
 
+ScriptdocHover_back= Back
+ScriptdocHover_forward= Forward
+
+ScriptdocHover_openDeclaration= Open Declaration
 
 ScriptTextHover_makeStickyHint= Press ''{0}'' for focus.
-
 NoBreakpointAnnotation_addBreakpoint= Add a breakpoint
-
 ScriptdocHover_noAttachedInformation= <em>Note: This element has no documentation and hence no information could be found.</em>
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/DLTKUIPlugin.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/DLTKUIPlugin.java
index d55ec05..24d27c7 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/DLTKUIPlugin.java
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/DLTKUIPlugin.java
@@ -65,6 +65,7 @@
 import org.eclipse.dltk.ui.editor.saveparticipant.SaveParticipantRegistry;
 import org.eclipse.dltk.ui.text.completion.ContentAssistHistory;
 import org.eclipse.dltk.ui.viewsupport.ImageDescriptorRegistry;
+import org.eclipse.dltk.ui.viewsupport.ImagesOnFileSystemRegistry;
 import org.eclipse.dltk.ui.viewsupport.ProblemMarkerManager;
 import org.eclipse.dltk.utils.ExecutionContexts;
 import org.eclipse.dltk.utils.IExecutableOperation;
@@ -825,4 +826,20 @@
 			fSaveParticipantRegistry = new SaveParticipantRegistry();
 		return fSaveParticipantRegistry;
 	}
+
+	private ImagesOnFileSystemRegistry fImagesOnFSRegistry;
+
+	/**
+	 * Returns the image registry that keeps its images on the local file
+	 * system.
+	 * 
+	 * @return the image registry
+	 * @since 4.0
+	 */
+	public ImagesOnFileSystemRegistry getImagesOnFSRegistry() {
+		if (fImagesOnFSRegistry == null) {
+			fImagesOnFSRegistry = new ImagesOnFileSystemRegistry();
+		}
+		return fImagesOnFSRegistry;
+	}
 }
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/AbstractDocumentationResponse.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/AbstractDocumentationResponse.java
index 5bf7b6e..8c89933 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/AbstractDocumentationResponse.java
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/AbstractDocumentationResponse.java
@@ -14,6 +14,8 @@
 import java.io.IOException;
 import java.net.URL;
 
+import org.eclipse.jface.resource.ImageDescriptor;
+
 /**
  * @since 2.0
  */
@@ -33,6 +35,10 @@
 		return null;
 	}
 
+	public ImageDescriptor getImage() {
+		return null;
+	}
+
 	public Object getObject() {
 		return object;
 	}
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/DocumentationResponseDelegate.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/DocumentationResponseDelegate.java
index 98f358d..4ac8420 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/DocumentationResponseDelegate.java
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/DocumentationResponseDelegate.java
@@ -15,6 +15,8 @@
 import java.io.Reader;
 import java.net.URL;
 
+import org.eclipse.jface.resource.ImageDescriptor;
+
 class DocumentationResponseDelegate implements IDocumentationResponse {
 	private final IDocumentationResponse target;
 
@@ -22,6 +24,10 @@
 		return target.getTitle();
 	}
 
+	public ImageDescriptor getImage() {
+		return target.getImage();
+	}
+
 	public Object getObject() {
 		return target.getObject();
 	}
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/IDocumentationResponse.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/IDocumentationResponse.java
index 370607d..8ccf411 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/IDocumentationResponse.java
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/IDocumentationResponse.java
@@ -15,6 +15,8 @@
 import java.io.Reader;
 import java.net.URL;
 
+import org.eclipse.jface.resource.ImageDescriptor;
+
 /**
  * Value object to return script documentation. All implementations should
  * extend {@link AbstractDocumentationResponse}
@@ -34,6 +36,15 @@
 	String getTitle();
 
 	/**
+	 * Returns the image for this documentation if available or
+	 * <code>null</code> otherwise.
+	 * 
+	 * @return
+	 * @since 4.0
+	 */
+	ImageDescriptor getImage();
+
+	/**
 	 * Returns the object this documentation applies to
 	 * 
 	 * @return
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/IScriptDocumentationTitleAdapter.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/IScriptDocumentationTitleAdapter.java
index ab33be9..0ebdc24 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/IScriptDocumentationTitleAdapter.java
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/IScriptDocumentationTitleAdapter.java
@@ -11,6 +11,8 @@
  *******************************************************************************/
 package org.eclipse.dltk.ui.documentation;
 
+import org.eclipse.jface.resource.ImageDescriptor;
+
 /**
  * @since 3.0
  */
@@ -18,4 +20,6 @@
 
 	String getTitle(Object element);
 
+	ImageDescriptor getImage(Object element);
+
 }
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/ScriptDocumentationAccess.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/ScriptDocumentationAccess.java
index 8ea33c4..229aa12 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/ScriptDocumentationAccess.java
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/ScriptDocumentationAccess.java
@@ -22,6 +22,7 @@
 import org.eclipse.dltk.core.ModelException;
 import org.eclipse.dltk.utils.AdaptUtils;
 import org.eclipse.dltk.utils.NatureExtensionManager;
+import org.eclipse.jface.resource.ImageDescriptor;
 
 /**
  * Helper needed to get access to script documentation.
@@ -180,6 +181,7 @@
 										IScriptDocumentationTitleAdapter.class);
 						if (titleAdapter != null) {
 							final String title = titleAdapter.getTitle(member);
+							// TODO (alex) image
 							if (title != null && title.length() != 0) {
 								return new DocumentationResponseDelegate(
 										response) {
@@ -187,6 +189,24 @@
 									public String getTitle() {
 										return title;
 									}
+
+									private boolean imageEvaluated;
+									private ImageDescriptor image;
+
+									@Override
+									public ImageDescriptor getImage() {
+										final ImageDescriptor result = super
+												.getImage();
+										if (result != null) {
+											return result;
+										}
+										if (!imageEvaluated) {
+											image = titleAdapter
+													.getImage(member);
+											imageEvaluated = true;
+										}
+										return image;
+									}
 								};
 							}
 						}
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/TextDocumentationResponse.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/TextDocumentationResponse.java
index d81e022..23c57d2 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/TextDocumentationResponse.java
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/documentation/TextDocumentationResponse.java
@@ -15,6 +15,8 @@
 import java.io.Reader;
 import java.io.StringReader;
 
+import org.eclipse.jface.resource.ImageDescriptor;
+
 /**
  * @since 3.0
  */
@@ -22,15 +24,22 @@
 
 	private final String content;
 	private final String title;
+	private final ImageDescriptor image;
 
 	public TextDocumentationResponse(Object object, String content) {
 		this(object, null, content);
 	}
 
 	public TextDocumentationResponse(Object object, String title, String content) {
+		this(object, title, null, content);
+	}
+
+	public TextDocumentationResponse(Object object, String title,
+			ImageDescriptor image, String content) {
 		super(object);
 		this.content = content;
 		this.title = title;
+		this.image = image;
 	}
 
 	public Reader getReader() throws IOException {
@@ -42,4 +51,9 @@
 		return title;
 	}
 
+	@Override
+	public ImageDescriptor getImage() {
+		return image;
+	}
+
 }
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/text/completion/AbstractScriptCompletionProposal.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/text/completion/AbstractScriptCompletionProposal.java
index fda1db7..dcb5d29 100644
--- a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/text/completion/AbstractScriptCompletionProposal.java
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/text/completion/AbstractScriptCompletionProposal.java
@@ -23,20 +23,19 @@
 import org.eclipse.dltk.core.DLTKCore;
 import org.eclipse.dltk.core.IModelElement;
 import org.eclipse.dltk.core.ModelException;
-import org.eclipse.dltk.internal.ui.text.hover.CompletionHoverControlCreator;
+import org.eclipse.dltk.internal.ui.text.hover.DocumentationHover;
 import org.eclipse.dltk.ui.DLTKUIPlugin;
 import org.eclipse.dltk.ui.PreferenceConstants;
 import org.eclipse.dltk.ui.text.ScriptTextTools;
+import org.eclipse.jface.internal.text.html.BrowserInformationControl;
 import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.jface.preference.PreferenceConverter;
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.BadPositionCategoryException;
-import org.eclipse.jface.text.DefaultInformationControl;
 import org.eclipse.jface.text.DefaultPositionUpdater;
 import org.eclipse.jface.text.DocumentEvent;
 import org.eclipse.jface.text.IDocument;
-import org.eclipse.jface.text.IInformationControl;
 import org.eclipse.jface.text.IInformationControlCreator;
 import org.eclipse.jface.text.IPositionUpdater;
 import org.eclipse.jface.text.IRegion;
@@ -67,9 +66,13 @@
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.RGB;
 import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchSite;
 import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
 import org.osgi.framework.Bundle;
 
+@SuppressWarnings("restriction")
 public abstract class AbstractScriptCompletionProposal implements
 		IScriptCompletionProposal, ICompletionProposalExtension,
 		ICompletionProposalExtension2, ICompletionProposalExtension3,
@@ -891,17 +894,26 @@
 	}
 
 	public IInformationControlCreator getInformationControlCreator() {
+		Shell shell = DLTKUIPlugin.getActiveWorkbenchShell();
+		if (shell == null || !BrowserInformationControl.isAvailable(shell))
+			return null;
 		if (fCreator == null) {
-			fCreator = new CompletionHoverControlCreator(
-					new IInformationControlCreator() {
-						public IInformationControl createInformationControl(
-								Shell parent) {
-							return new DefaultInformationControl(parent, true);
-						}
-					}, true);
+			DocumentationHover.PresenterControlCreator presenterControlCreator = new DocumentationHover.PresenterControlCreator(
+					getSite());
+			fCreator = new DocumentationHover.HoverControlCreator(
+					presenterControlCreator, true);
 		}
 		return fCreator;
-		// return null;
+	}
+
+	private IWorkbenchSite getSite() {
+		final IWorkbenchPage page = DLTKUIPlugin.getActivePage();
+		if (page != null) {
+			final IWorkbenchPart part = page.getActivePart();
+			if (part != null)
+				return part.getSite();
+		}
+		return null;
 	}
 
 	public String getSortString() {
diff --git a/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/viewsupport/ImagesOnFileSystemRegistry.java b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/viewsupport/ImagesOnFileSystemRegistry.java
new file mode 100644
index 0000000..fbad913
--- /dev/null
+++ b/core/plugins/org.eclipse.dltk.ui/src/org/eclipse/dltk/ui/viewsupport/ImagesOnFileSystemRegistry.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 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.dltk.ui.viewsupport;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+
+import org.eclipse.dltk.ui.DLTKUIPlugin;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+
+/**
+ * Image registry that keeps its images on the local file system.
+ * 
+ * @since 4.0
+ */
+public class ImagesOnFileSystemRegistry {
+
+	private static final String IMAGE_DIR = "dltk-images"; //$NON-NLS-1$
+
+	private HashMap<ImageDescriptor, URL> fURLMap;
+	private final File fTempDir;
+	private int fImageCount;
+
+	public ImagesOnFileSystemRegistry() {
+		fURLMap = new HashMap<ImageDescriptor, URL>();
+		fTempDir = getTempDir();
+		fImageCount = 0;
+	}
+
+	private File getTempDir() {
+		try {
+			File imageDir = DLTKUIPlugin.getDefault().getStateLocation()
+					.append(IMAGE_DIR).toFile();
+			if (imageDir.exists()) {
+				// has not been deleted on previous shutdown
+				delete(imageDir);
+			}
+			if (!imageDir.exists()) {
+				imageDir.mkdir();
+			}
+			if (!imageDir.isDirectory()) {
+				DLTKUIPlugin
+						.logErrorMessage("Failed to create image directory " + imageDir.toString()); //$NON-NLS-1$
+				return null;
+			}
+			return imageDir;
+		} catch (IllegalStateException e) {
+			// no state location
+			return null;
+		}
+	}
+
+	private void delete(File file) {
+		if (file.isDirectory()) {
+			File[] listFiles = file.listFiles();
+			for (int i = 0; i < listFiles.length; i++) {
+				delete(listFiles[i]);
+			}
+		}
+		file.delete();
+	}
+
+	public URL getImageURL(ImageDescriptor descriptor) {
+		if (fTempDir == null)
+			return null;
+
+		URL url = fURLMap.get(descriptor);
+		if (url != null)
+			return url;
+
+		File imageFile = getNewFile();
+		ImageData imageData = descriptor.getImageData();
+		if (imageData == null) {
+			return null;
+		}
+
+		ImageLoader loader = new ImageLoader();
+		loader.data = new ImageData[] { imageData };
+		loader.save(imageFile.getAbsolutePath(), SWT.IMAGE_PNG);
+
+		try {
+			url = imageFile.toURI().toURL();
+			fURLMap.put(descriptor, url);
+			return url;
+		} catch (MalformedURLException e) {
+			DLTKUIPlugin.log(e);
+		}
+		return null;
+	}
+
+	private File getNewFile() {
+		File file;
+		do {
+			file = new File(fTempDir, String.valueOf(getImageCount()) + ".png"); //$NON-NLS-1$
+		} while (file.exists());
+		return file;
+	}
+
+	private synchronized int getImageCount() {
+		return fImageCount++;
+	}
+
+	public void dispose() {
+		if (fTempDir != null) {
+			delete(fTempDir);
+		}
+		fURLMap = null;
+	}
+}