blob: 93e5130cdd66f0985ac9a67ca81a1df3eaf75125 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2016 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.help.ui.internal.views;
import org.eclipse.help.ui.internal.Messages;
import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.DialogTray;
import org.eclipse.jface.dialogs.IDialogPage;
import org.eclipse.jface.dialogs.IPageChangeProvider;
import org.eclipse.jface.dialogs.IPageChangedListener;
import org.eclipse.jface.dialogs.PageChangedEvent;
import org.eclipse.jface.dialogs.TrayDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
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.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.HyperlinkGroup;
import org.eclipse.ui.forms.widgets.FormToolkit;
/**
* The tray that appears on the side of dialogs when the user summons context
* help or a cheat sheet follows the user into a dialog.
*/
public class HelpTray extends DialogTray implements IPageChangedListener {
public static final int MINIMUM_HEIGHT = 450;
private static final int DEFAULT_WIDTH = 210;
private int originalHeight;
private int heightAdded;
private FormToolkit toolkit;
private ReusableHelpPart helpPart;
private Shell shell;
private IContributionItem closeAction;
private Image normal;
private Image hover;
/**
* Creates any actions needed by the tray.
*/
private void createActions() {
createImages();
closeAction = new ContributionItem() {
@Override
public void fill(ToolBar parent, int index) {
final ToolItem item = new ToolItem(parent, SWT.PUSH);
item.setImage(normal);
item.setHotImage(hover);
item.setToolTipText(Messages.ReusableHelpPart_closeAction_tooltip);
item.addListener(SWT.Selection, event -> {
// close the tray
TrayDialog dialog = (TrayDialog) shell.getData();
dialog.closeTray();
});
}
};
}
/**
* Creates the contents of the tray.
*
* @param parent the parent composite that will contain the tray
*/
@Override
protected Control createContents(Composite parent) {
// if the dialog is too short, make it taller
ensureMinimumHeight(parent.getShell());
toolkit = new FormToolkit(parent.getDisplay());
toolkit.getHyperlinkGroup().setHyperlinkUnderlineMode(HyperlinkGroup.UNDERLINE_HOVER);
toolkit.getColors().initializeSectionToolBarColors();
Composite container = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout();
layout.marginWidth = layout.marginHeight = 0;
layout.verticalSpacing = 0;
container.setLayout(layout);
container.addListener(SWT.Dispose, event -> dispose());
ToolBarManager tbm = new ToolBarManager(SWT.FLAT);
tbm.createControl(container);
GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_END);
gd.grabExcessHorizontalSpace = true;
tbm.getControl().setLayoutData(gd);
Label separator = new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL);
gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gd.heightHint = 1;
separator.setLayoutData(gd);
helpPart = new ReusableHelpPart(PlatformUI.getWorkbench().getProgressService());
helpPart.init(null, tbm, null, null, null);
helpPart.setDefaultContextHelpText(Messages.HelpView_defaultText);
helpPart.createControl(container, toolkit);
gd = new GridData(GridData.FILL_BOTH);
gd.widthHint = DEFAULT_WIDTH;
helpPart.getControl().setLayoutData(gd);
createActions();
tbm.add(closeAction);
shell = parent.getShell();
hookPageChangeListener(shell);
helpPart.getControl().addListener(SWT.Dispose, event -> unhookPageChangeListener(shell));
return container;
}
/**
* Creates any custom needed by the tray, such as the close button.
*/
private void createImages() {
Display display = Display.getCurrent();
int[] shape = new int[] {
3, 3, 5, 3, 7, 5, 8, 5, 10, 3, 12, 3,
12, 5, 10, 7, 10, 8, 12,10, 12,12,
10,12, 8, 10, 7, 10, 5, 12, 3, 12,
3, 10, 5, 8, 5, 7, 3, 5
};
/*
* Use magenta as transparency color since it is used infrequently.
*/
Color border = display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW);
Color background = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
Color backgroundHot = new Color(display, new RGB(252, 160, 160));
Color transparent = display.getSystemColor(SWT.COLOR_MAGENTA);
PaletteData palette = new PaletteData(new RGB[] { transparent.getRGB(), border.getRGB(), background.getRGB(), backgroundHot.getRGB() });
ImageData data = new ImageData(16, 16, 8, palette);
data.transparentPixel = 0;
normal = new Image(display, data);
normal.setBackground(transparent);
GC gc = new GC(normal);
gc.setBackground(background);
gc.fillPolygon(shape);
gc.setForeground(border);
gc.drawPolygon(shape);
gc.dispose();
hover = new Image(display, data);
hover.setBackground(transparent);
gc = new GC(hover);
gc.setBackground(backgroundHot);
gc.fillPolygon(shape);
gc.setForeground(border);
gc.drawPolygon(shape);
gc.dispose();
backgroundHot.dispose();
}
/**
* Disposes any resources used by the tray.
*/
private void dispose() {
normal.dispose();
hover.dispose();
toolkit.dispose();
helpPart.dispose();
/*
* Shell is about to be closed. Add a one-time-only listener
* that will return the dialog height back to original.
*/
shell.addListener(SWT.Resize, new Listener() {
@Override
public void handleEvent(Event event) {
shell.removeListener(SWT.Resize, this);
Point p = shell.getSize();
if (heightAdded > 0 && p.y > originalHeight) {
p.y = Math.max(p.y - heightAdded, originalHeight);
shell.setSize(p);
}
}
});
}
/**
* Ensures that the dialog's height is sufficient to contain the help tray.
* If the dialog is too short, its height is increased. When closing the tray,
* the height is returned to original (see dispose()).
*
* @param shell the dialog's shell
*/
private void ensureMinimumHeight(Shell shell) {
Point p = shell.getSize();
originalHeight = p.y;
if (p.y < MINIMUM_HEIGHT) {
heightAdded = MINIMUM_HEIGHT - p.y;
p.y = MINIMUM_HEIGHT;
shell.setSize(p);
}
else {
heightAdded = 0;
}
}
/**
* Returns the ReusableHelpPart contained in the tray.
*
* @return the tray's ReusableHelpPart
*/
public ReusableHelpPart getHelpPart() {
return helpPart;
}
/**
* Add the listener that gets notified of page changes (to automatically
* update context help).
*
* @param parent the Composite to hook the listener to
*/
private void hookPageChangeListener(Composite parent) {
Object data = parent.getData();
if (data instanceof IPageChangeProvider) {
((IPageChangeProvider)data).addPageChangedListener(this);
}
}
/**
* Returns whether or not the help tray can handle the given shell. In some
* cases the help tray is not appropriate for shells that are too short and
* not resizable. In these cases infopops are used.
*
* @param shell the shell to check
* @return whether or not the help tray is appropriate for the hsell
*/
public static boolean isAppropriateFor(Shell shell) {
if (shell != null && !shell.isDisposed() && shell.isVisible()) {
Object data = shell.getData();
return (data instanceof TrayDialog && (shell.getSize().y >= MINIMUM_HEIGHT || (shell.getStyle() & SWT.RESIZE) != 0));
}
return false;
}
/**
* Called whenever the dialog we're inside has changed pages. This updates
* the context help page if it is visible.
*
* @param event the page change event
*/
@Override
public void pageChanged(PageChangedEvent event) {
Object page = event.getSelectedPage();
Control c = null;
if (page instanceof IDialogPage) {
c = ((IDialogPage) page).getControl();
} else {
c = shell.getDisplay().getFocusControl();
if (c instanceof TabFolder) {
TabFolder folder = (TabFolder) c;
TabItem[] selection = folder.getSelection();
if (selection.length == 1) {
c = selection[0].getControl();
}
}
}
helpPart.update(null, null, null, c, false);
}
/**
* Remove the listener that gets notified of page changes (to automatically
* update context help).
*
* @param parent the Composite that had the listener
*/
private void unhookPageChangeListener(Composite parent) {
Object data = parent.getData();
if (data instanceof IPageChangeProvider) {
((IPageChangeProvider)data).removePageChangedListener(this);
}
}
}