blob: 634ac2de787c43b1be0073d6ababa95297da663a [file] [log] [blame]
/*
* Copyright (c) 2014, 2015 Eike Stepper (Berlin, Germany) 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:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.oomph.ui;
import org.eclipse.oomph.internal.ui.UIPlugin;
import org.eclipse.oomph.internal.util.HTTPServer;
import org.eclipse.oomph.util.OS;
import org.eclipse.jface.dialogs.DialogTray;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.HelpEvent;
import org.eclipse.swt.events.HelpListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Scrollable;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Tree;
import java.util.HashSet;
import java.util.Set;
/**
* @author Eike Stepper
*/
public class HelpSupport
{
private static final int INACTIVITY_SECONDS = 5;
private static final String HELP_CONTEXT = "/help";
private final TitleAreaDialog dialog;
private final Set<Control> calloutControls = new HashSet<Control>();
private final Image[] calloutImages = new Image[10];
private final InactivityDetector inactivityDetector = new InactivityDetector(1000, INACTIVITY_SECONDS * 1000)
{
@Override
protected void handleInactivity(Display display, boolean inactive)
{
if (inactive)
{
display.asyncExec(helpAnimator);
}
HelpSupport.this.handleInactivity(display, inactive);
}
};
private HelpAnimator helpAnimator;
private HTTPServer helpServer;
private Browser helpBrowser;
public HelpSupport(TitleAreaDialog dialog)
{
this.dialog = dialog;
dialog.setHelpAvailable(true);
}
public final void hook(final ToolItem helpButton)
{
helpAnimator = new HelpAnimator(helpButton);
Shell shell = dialog.getShell();
shell.addHelpListener(new HelpListener()
{
public void helpRequested(HelpEvent e)
{
if (UIUtil.isBrowserAvailable())
{
if (dialog.getTray() != null)
{
dialog.closeTray();
helpButton.setSelection(false);
return;
}
DialogTray tray = new DialogTray()
{
@Override
protected Control createContents(Composite parent)
{
helpBrowser = new Browser(parent, SWT.NONE);
helpBrowser.setSize(500, 800);
helpBrowser.addDisposeListener(new DisposeListener()
{
public void widgetDisposed(DisposeEvent e)
{
helpBrowser = null;
redrawCalloutControls();
}
});
updateHelp();
return helpBrowser;
}
};
dialog.openTray(tray);
helpButton.setSelection(true);
}
else
{
helpButton.setSelection(false);
String helpPath = getHelpPath();
if (helpPath != null)
{
OS.INSTANCE.openSystemBrowser(getHelpURL(helpPath));
}
}
}
});
detectInactivity(shell);
}
public final boolean isHelpOpen()
{
return helpBrowser != null;
}
public final void updateHelp()
{
String helpPath = getHelpPath();
if (helpPath != null)
{
setHelpPath(helpPath);
}
}
private final String getHelpPath()
{
if (dialog instanceof HelpProvider)
{
HelpProvider helpProvider = (HelpProvider)dialog;
String helpPath = helpProvider.getHelpPath();
return helpPath;
}
return null;
}
public final void addHelpCallout(final Control control, final int number)
{
control.addPaintListener(new PaintListener()
{
public void paintControl(PaintEvent e)
{
if (isHelpOpen())
{
Image image = getCalloutImage(number);
if (image != null)
{
Rectangle bounds = getBounds(control);
e.gc.drawImage(image, bounds.width - 31, bounds.y + 10);
}
}
}
});
control.addControlListener(new ControlAdapter()
{
@Override
public void controlResized(ControlEvent e)
{
if (isHelpOpen())
{
control.redraw();
}
}
});
if (control instanceof Scrollable)
{
Scrollable scrollable = (Scrollable)control;
ScrollBar verticalBar = scrollable.getVerticalBar();
ScrollBar horizontalBar = scrollable.getHorizontalBar();
if (verticalBar != null || horizontalBar != null)
{
SelectionAdapter listener = new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
if (isHelpOpen())
{
control.redraw();
}
}
};
if (verticalBar != null)
{
verticalBar.addSelectionListener(listener);
}
if (horizontalBar != null)
{
horizontalBar.addSelectionListener(listener);
}
}
}
calloutControls.add(control);
}
public final void detectInactivity(Control control)
{
inactivityDetector.monitor(control);
}
public void dispose()
{
if (helpServer != null)
{
try
{
helpServer.stop();
}
catch (Exception ex)
{
UIPlugin.INSTANCE.log(ex);
}
helpServer = null;
}
for (int i = 0; i < calloutImages.length; i++)
{
calloutImages[i] = null;
}
}
protected void handleInactivity(Display display, boolean inactive)
{
}
private Image getCalloutImage(int number)
{
if (calloutImages[number] == null)
{
calloutImages[number] = UIPlugin.INSTANCE.getSWTImage("callout-" + number + ".png");
}
return calloutImages[number];
}
private void redrawCalloutControls()
{
for (Control control : calloutControls)
{
if (!control.isDisposed() && control.isVisible())
{
control.redraw();
}
}
}
private synchronized String getHelpURL(String path)
{
if (helpServer == null)
{
try
{
helpServer = new HTTPServer();
helpServer.addContext(new HTTPServer.PluginContext(HELP_CONTEXT, true));
}
catch (Exception ex)
{
UIPlugin.INSTANCE.log(ex);
return null;
}
}
return "http://localhost:" + helpServer.getPort() + HELP_CONTEXT + path;
}
private void setHelpPath(String path)
{
if (helpBrowser != null)
{
String url = getHelpURL(path);
if (url != null)
{
if (!url.equals(helpBrowser.getUrl()))
{
Browser.clearSessions();
helpBrowser.setUrl(url);
}
}
else
{
helpBrowser.setText("<h3>Help content not found.</h3>");
}
}
}
private static Rectangle getBounds(final Control control)
{
Rectangle bounds = control instanceof Scrollable ? ((Scrollable)control).getClientArea() : control.getBounds();
--bounds.width;
--bounds.height;
int y = getHeaderHeight(control);
bounds.y += y;
bounds.height -= y;
return bounds;
}
private static int getHeaderHeight(Control control)
{
if (control instanceof Tree)
{
return ((Tree)control).getHeaderHeight();
}
if (control instanceof Table)
{
return ((Table)control).getHeaderHeight();
}
return 0;
}
/**
* @author Eike Stepper
*/
public interface HelpProvider
{
public String getHelpPath();
}
/**
* @author Eike Stepper
*/
private final class HelpAnimator extends ButtonAnimator
{
public HelpAnimator(ToolItem helpButton)
{
super(UIPlugin.INSTANCE, helpButton, "help", 10);
}
@Override
public Shell getShell()
{
return dialog.getShell();
}
@Override
protected boolean doAnimate()
{
return inactivityDetector.isInactive() && !isHelpOpen();
}
}
}