blob: a697da6169744a7c5246abcd86c50edf2df88666 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.help.ui.internal;
import org.eclipse.core.runtime.Platform;
import org.eclipse.help.IContext;
import org.eclipse.help.IContext2;
import org.eclipse.help.IHelpResource;
import org.eclipse.help.UAContentFilter;
import org.eclipse.help.internal.base.BaseHelpSystem;
import org.eclipse.help.internal.base.HelpEvaluationContext;
import org.eclipse.help.ui.internal.views.HelpTray;
import org.eclipse.osgi.service.environment.Constants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.ACC;
import org.eclipse.swt.accessibility.Accessible;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleControlAdapter;
import org.eclipse.swt.accessibility.AccessibleControlEvent;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
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.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
/**
* ContextHelpDialog
*/
public class ContextHelpDialog {
//private static ImageRegistry imgRegistry = null;
private Color backgroundColour = null;
private IContext context;
private Color foregroundColour = null;
private Color linkColour = null;
private static HyperlinkHandler linkManager = new HyperlinkHandler();
protected Shell parentShell;
protected Shell shell;
protected String infopopText;
/**
* Listener for hyperlink selection.
*/
class LinkListener extends HyperlinkAdapter {
IHelpResource topic;
public LinkListener(IHelpResource topic) {
this.topic = topic;
}
@Override
public void linkActivated(Control c) {
launchLinks(topic);
}
}
/**
* Constructor:
*
* @param context
* an array of String or an array of IContext
* @param x
* the x mouse location in the current display
* @param y
* the y mouse location in the current display
*/
ContextHelpDialog(IContext context, int x, int y) {
this.context = context;
Display display = Display.getCurrent();
if (display == null) {
return;
}
backgroundColour = display.getSystemColor(SWT.COLOR_INFO_BACKGROUND);
foregroundColour = display.getSystemColor(SWT.COLOR_INFO_FOREGROUND);
linkColour = display.getSystemColor(SWT.COLOR_BLUE);
parentShell = PlatformUI.getWorkbench().getModalDialogShellProvider().getShell();
if (parentShell != null) {
boolean isModal = 0 < (parentShell.getStyle() & (SWT.APPLICATION_MODAL
| SWT.PRIMARY_MODAL | SWT.SYSTEM_MODAL));
if (HelpUIPlugin.DEBUG_INFOPOP) {
System.out
.println("ContextHelpDialog.ContextHelpDialog(): ParentShell: " //$NON-NLS-1$
+ parentShell.toString() + " is " //$NON-NLS-1$
+ (isModal ? "modal" : "modeless")); //$NON-NLS-1$ //$NON-NLS-2$
}
}
shell = new Shell(parentShell, SWT.NONE);
if (HelpUIPlugin.DEBUG_INFOPOP) {
System.out
.println("ContextHelpDialog.ContextHelpDialog(): Shell is:" //$NON-NLS-1$
+ shell.toString());
}
PlatformUI.getWorkbench().getHelpSystem().setHelp(shell, IHelpUIConstants.F1_SHELL);
shell.addListener(SWT.Deactivate, e -> {
if (HelpUIPlugin.DEBUG_INFOPOP) {
System.out.println("ContextHelpDialog shell deactivate listener: SWT.Deactivate called. "); //$NON-NLS-1$
}
close();
});
shell.addTraverseListener(e -> {
if (e.detail == SWT.TRAVERSE_ESCAPE) {
if (HelpUIPlugin.DEBUG_INFOPOP) {
System.out.println(
"ContextHelpDialog: shell traverse listener: SWT.TRAVERSE_ESCAPE called. "); //$NON-NLS-1$
}
e.doit = true;
}
});
shell.addControlListener(new ControlAdapter() {
@Override
public void controlMoved(ControlEvent e) {
if (HelpUIPlugin.DEBUG_INFOPOP) {
System.out
.println("ContextHelpDialog: shell control adapter called."); //$NON-NLS-1$
}
Rectangle clientArea = shell.getClientArea();
shell.redraw(clientArea.x, clientArea.y, clientArea.width,
clientArea.height, true);
shell.update();
}
});
if (HelpUIPlugin.DEBUG_INFOPOP) {
System.out
.println("ContextHelpDialog.ContextHelpDialog(): Focus owner is: " //$NON-NLS-1$
+ Display.getCurrent().getFocusControl().toString());
}
linkManager
.setHyperlinkUnderlineMode(HyperlinkHandler.UNDERLINE_ALWAYS);
createContents(shell);
shell.pack();
// Correct x and y of the shell if it not contained within the screen
int width = shell.getBounds().width;
int height = shell.getBounds().height;
Rectangle screen = display.getClientArea();
// check lower boundaries
x = x >= screen.x ? x : screen.x;
y = y >= screen.y ? y : screen.y;
// check upper boundaries
x = x + width <= screen.width ? x : screen.width - width;
y = y + height <= screen.height ? y : screen.height - height;
shell.setLocation(x, y);
initAccessible(shell);
}
public synchronized void close() {
try {
if (HelpUIPlugin.DEBUG_INFOPOP) {
System.out.println("ContextHelpDialog.close()"); //$NON-NLS-1$
}
if (shell != null) {
shell.close();
if (!shell.isDisposed())
shell.dispose();
shell = null;
}
} catch (Throwable ex) {
}
}
protected Control createContents(Composite contents) {
initAccessible(contents);
contents.setBackground(backgroundColour);
GridLayout layout = new GridLayout();
layout.marginHeight = 5;
layout.marginWidth = 5;
contents.setLayout(layout);
contents.setLayoutData(new GridData(GridData.FILL_BOTH));
// create the dialog area and button bar
createInfoArea(contents);
Control c = createLinksArea(contents);
if (c != null) {
// links exist, make them the only focusable controls
contents.setTabList(new Control[] { c });
}
return contents;
}
private Control createInfoArea(Composite parent) {
// Create the text field.
String styledText = null;
if (context instanceof IContext2) {
styledText = ((IContext2) context).getStyledText();
}
if (styledText == null && context.getText() != null) {
styledText = context.getText();
styledText= styledText.replaceAll("<b>","<@#\\$b>"); //$NON-NLS-1$ //$NON-NLS-2$
styledText= styledText.replaceAll("</b>", "</@#\\$b>"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (styledText == null) { // no description found in context objects.
styledText = Messages.ContextHelpPart_noDescription;
}
Description text = new Description(parent, SWT.MULTI | SWT.READ_ONLY);
text.addTraverseListener(e -> {
if (e.detail == SWT.TRAVERSE_ESCAPE) {
if (HelpUIPlugin.DEBUG_INFOPOP) {
System.out.println(
"ContextHelpDialog text TraverseListener.handleEvent(): SWT.TRAVERSE_ESCAPE."); //$NON-NLS-1$
}
e.doit = true;
}
});
text.getCaret().setVisible(false);
text.setBackground(backgroundColour);
text.setForeground(foregroundColour);
text.setFont(parent.getFont());
int linkWidth = getLinksWidth(text);
StyledLineWrapper content = new StyledLineWrapper(styledText, text,
linkWidth + 70);
text.setContent(content);
text.setStyleRanges(content.getStyles());
infopopText = text.getText();
initAccessible(text);
return text;
}
/**
* Measures the longest label of related links
*
* @param text
* @return
*/
private int getLinksWidth(Description text) {
int linkWidth = 0;
IHelpResource relatedTopics[] = context.getRelatedTopics();
if (relatedTopics != null) {
GC gc = new GC(text);
for (int i = 0; i < relatedTopics.length; i++) {
linkWidth = Math.max(linkWidth, gc.textExtent(relatedTopics[i]
.getLabel()).x);
}
gc.dispose();
}
return linkWidth;
}
private Control createLink(Composite parent, IHelpResource topic) {
Label image = new Label(parent, SWT.NONE);
image.setImage(getImage());
image.setBackground(backgroundColour);
GridData data = new GridData();
data.horizontalAlignment = GridData.HORIZONTAL_ALIGN_BEGINNING;
data.verticalAlignment = GridData.VERTICAL_ALIGN_BEGINNING;
//data.horizontalIndent = 4;
image.setLayoutData(data);
HyperlinkLabel link = new HyperlinkLabel(parent, SWT.NONE);
link.setText(topic.getLabel());
link.setBackground(backgroundColour);
link.setForeground(linkColour);
link.setFont(parent.getFont());
linkManager.registerHyperlink(link, new LinkListener(topic));
return link;
}
private Control createLinksArea(Composite parent) {
IHelpResource[] relatedTopics = context.getRelatedTopics();
if (relatedTopics == null)
return null;
// Create control
Composite composite = new Composite(parent, SWT.NONE);
initAccessible(composite);
composite.setBackground(backgroundColour);
GridLayout layout = new GridLayout();
layout.marginHeight = 2;
layout.marginWidth = 0;
layout.verticalSpacing = 3;
layout.horizontalSpacing = 2;
layout.numColumns = 2;
composite.setLayout(layout);
composite.setFont(parent.getFont());
GridData data = new GridData(GridData.FILL_BOTH
| GridData.HORIZONTAL_ALIGN_BEGINNING
| GridData.VERTICAL_ALIGN_CENTER);
composite.setLayoutData(data);
// Create separator.
Label label = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL);
label.setBackground(backgroundColour);
label.setForeground(foregroundColour);
data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING
| GridData.VERTICAL_ALIGN_BEGINNING | GridData.FILL_HORIZONTAL);
data.horizontalSpan = 2;
label.setLayoutData(data);
// Create related links
for (int i = 0; i < relatedTopics.length; i++) {
if (!UAContentFilter.isFiltered(relatedTopics[i], HelpEvaluationContext.getContext())) {
createLink(composite, relatedTopics[i]);
}
}
// create dynamic help link if current context allows dynamic help
IWorkbenchWindow wbWindow = HelpUIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow();
if (DefaultHelpUI.isActiveShell(parentShell, wbWindow) || HelpTray.isAppropriateFor(parentShell)) {
// Create separator.
label = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL);
label.setBackground(backgroundColour);
label.setForeground(foregroundColour);
data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING
| GridData.VERTICAL_ALIGN_BEGINNING | GridData.FILL_HORIZONTAL);
data.horizontalSpan = 2;
label.setLayoutData(data);
// create link to the dynamic help
createDynamicHelpLink(composite);
}
return composite;
}
private Control createDynamicHelpLink(Composite parent) {
Label image = new Label(parent, SWT.NONE);
Image img = HelpUIResources.getImage(IHelpUIConstants.IMAGE_DHELP);
image.setImage(img);
image.setBackground(backgroundColour);
GridData data = new GridData();
data.horizontalAlignment = GridData.HORIZONTAL_ALIGN_BEGINNING;
data.verticalAlignment = GridData.VERTICAL_ALIGN_BEGINNING;
//data.horizontalIndent = 4;
image.setLayoutData(data);
HyperlinkLabel link = new HyperlinkLabel(parent, SWT.NONE);
link.setText(Messages.ContextHelpDialog_showInDynamicHelp);
link.setBackground(backgroundColour);
link.setForeground(linkColour);
link.setFont(parent.getFont());
linkManager.registerHyperlink(link, new HyperlinkAdapter() {
@Override
public void linkActivated(Control label) {
openDynamicHelp();
}
});
return link;
}
/**
* Called when related link has been chosen Opens help viewer with list of
* all related topics
*/
protected void launchLinks(IHelpResource selectedTopic) {
close();
if (HelpUIPlugin.DEBUG_INFOPOP) {
System.out.println("ContextHelpDialog.launchLinks(): closed shell"); //$NON-NLS-1$
}
BaseHelpSystem.getHelpDisplay().displayHelp(
context,
selectedTopic,
DefaultHelpUI.isDisplayModal(parentShell)
&& !Constants.OS_WIN32.equalsIgnoreCase(Platform
.getOS()));
}
private void openDynamicHelp() {
BusyIndicator.showWhile(shell.getDisplay(), () -> {
close();
DefaultHelpUI.getInstance().displayContext(context, 0, 0, true);
});
}
public synchronized void open() {
try {
shell.open();
if (HelpUIPlugin.DEBUG_INFOPOP) {
System.out
.println("ContextHelpDialog.open(): Focus owner after open is: " //$NON-NLS-1$
+ Display.getCurrent().getFocusControl()
.toString());
}
} catch (Throwable e) {
HelpUIPlugin
.logError(
"An error occurred when opening context-sensitive help pop-up.", //$NON-NLS-1$
e);
}
}
private Image getImage() {
return HelpUIResources.getImage(IHelpUIConstants.IMAGE_FILE_F1TOPIC);
}
public boolean isShowing() {
return (shell != null && !shell.isDisposed() && shell.isVisible());
}
private void initAccessible(final Control control) {
Accessible accessible = control.getAccessible();
accessible.addAccessibleListener(new AccessibleAdapter() {
@Override
public void getName(AccessibleEvent e) {
e.result = infopopText;
}
@Override
public void getHelp(AccessibleEvent e) {
e.result = control.getToolTipText();
}
});
accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
@Override
public void getChildAtPoint(AccessibleControlEvent e) {
Point pt = control.toControl(new Point(e.x, e.y));
e.childID = (control.getBounds().contains(pt)) ? ACC.CHILDID_MULTIPLE
: ACC.CHILDID_NONE;
}
@Override
public void getLocation(AccessibleControlEvent e) {
Rectangle location = control.getBounds();
Point pt = control.toDisplay(new Point(location.x, location.y));
e.x = pt.x;
e.y = pt.y;
e.width = location.width;
e.height = location.height;
}
@Override
public void getChildCount(AccessibleControlEvent e) {
e.detail = 1;
}
@Override
public void getRole(AccessibleControlEvent e) {
e.detail = ACC.ROLE_LABEL;
}
@Override
public void getState(AccessibleControlEvent e) {
e.detail = ACC.STATE_READONLY;
}
});
}
public class Description extends StyledText {
/**
* @param parent
* @param style
*/
public Description(Composite parent, int style) {
super(parent, style);
}
@Override
public boolean setFocus() {
return false;
}
@Override
public boolean isFocusControl() {
return false;
}
}
}