blob: b74177034ed6c5f526d2b653b48faaa9b8cce4ec [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 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
* Andrey Loskutov <loskutov@gmx.de> - Bug 445538
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 472654
* Patrik Suzzi <psuzzi@gmail.com> - Bug 489250
*******************************************************************************/
package org.eclipse.ui.internal.dialogs.cpd;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.e4.ui.workbench.renderers.swt.HandledContributionItem;
import org.eclipse.jface.bindings.Binding;
import org.eclipse.jface.bindings.BindingManager;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.WorkbenchWindow;
import org.eclipse.ui.internal.dialogs.cpd.CustomizePerspectiveDialog.ActionSet;
import org.eclipse.ui.internal.dialogs.cpd.CustomizePerspectiveDialog.DisplayItem;
import org.eclipse.ui.internal.dialogs.cpd.CustomizePerspectiveDialog.DynamicContributionItem;
import org.eclipse.ui.internal.dialogs.cpd.CustomizePerspectiveDialog.ShortcutItem;
import org.eclipse.ui.internal.dialogs.cpd.TreeManager.TreeItem;
import org.eclipse.ui.internal.keys.BindingService;
import org.eclipse.ui.internal.util.Util;
import org.eclipse.ui.keys.IBindingService;
/**
* A tooltip with useful information based on the type of ContributionItem
* the cursor hovers over in a Tree. In addition to the content provided by
* the {@link NameAndDescriptionToolTip} this includes action set
* information and key binding data.
*
* @since 3.5
*/
class ItemDetailToolTip extends NameAndDescriptionToolTip {
private Tree tree;
private boolean showActionSet;
private boolean showKeyBindings;
private ViewerFilter filter;
private TreeViewer v;
private CustomizePerspectiveDialog dialog;
/**
* @param dialog
* @param tree
* The tree for the tooltip to hover over
*/
ItemDetailToolTip(CustomizePerspectiveDialog dialog, TreeViewer v, Tree tree, boolean showActionSet,
boolean showKeyBindings, ViewerFilter filter) {
super(tree,NO_RECREATE);
this.dialog = dialog;
this.tree = tree;
this.v = v;
this.showActionSet = showActionSet;
this.showKeyBindings = showKeyBindings;
this.filter = filter;
this.setHideOnMouseDown(false);
}
@Override
public Point getLocation(Point tipSize, Event event) {
// try to position the tooltip at the bottom of the cell
ViewerCell cell = v.getCell(new Point(event.x, event.y));
if( cell != null ) {
return tree.toDisplay(event.x,cell.getBounds().y+cell.getBounds().height);
}
return super.getLocation(tipSize, event);
}
@Override
protected Object getToolTipArea(Event event) {
// Ensure that the tooltip is hidden when the cell is left
return v.getCell(new Point(event.x, event.y));
}
@Override
protected void addContent(Composite destination, Object modelElement) {
final DisplayItem item = (DisplayItem) modelElement;
// Show any relevant action set info
if (showActionSet) {
String text = null;
Image image = null;
if(CustomizePerspectiveDialog.isEffectivelyAvailable(item, filter)) {
if(item.actionSet != null) {
//give information on which command group the item is in
final String actionSetName = item.getActionSet().descriptor
.getLabel();
text = NLS.bind(
WorkbenchMessages.HideItems_itemInActionSet,
actionSetName);
}
} else {
//give feedback on why item is unavailable
image = dialog.warningImageDescriptor.createImage();
if (item.getChildren().isEmpty() && item.getActionSet() != null) {
//i.e. is a leaf
final String actionSetName = item.getActionSet().
descriptor.getLabel();
text = NLS.bind(
WorkbenchMessages.HideItems_itemInUnavailableActionSet,
actionSetName);
} else if (item.getChildren().isEmpty() && item.getActionSet() == null
&& item.getIContributionItem() instanceof HandledContributionItem) {
text = WorkbenchMessages.HideItems_itemInUnavailableCommand;
} else {
//i.e. has children
Set<ActionSet> actionGroup = new LinkedHashSet<>();
ItemDetailToolTip.collectDescendantCommandGroups(actionGroup, item,
filter);
if (actionGroup.size() == 1) {
//i.e. only one child
ActionSet actionSet = actionGroup.
iterator().next();
text = NLS.bind(
WorkbenchMessages.HideItems_unavailableChildCommandGroup,
actionSet.descriptor.getId(),
actionSet.descriptor.getLabel());
} else {
//i.e. multiple children
String commandGroupList = null;
for (ActionSet actionSet : actionGroup) {
// For each action set, make a link for it, set
// the href to its id
String commandGroupLink = MessageFormat.format(
"<a href=\"{0}\">{1}</a>", //$NON-NLS-1$
actionSet.descriptor.getId(), actionSet.descriptor.getLabel());
if (commandGroupList == null) {
commandGroupList = commandGroupLink;
} else {
commandGroupList = Util.createList(
commandGroupList, commandGroupLink);
}
}
commandGroupList = NLS.bind(
"{0}{1}", new Object[] { CustomizePerspectiveDialog.NEW_LINE, commandGroupList }); //$NON-NLS-1$
text = NLS.bind(
WorkbenchMessages.HideItems_unavailableChildCommandGroups,
commandGroupList);
}
}
}
if(text != null) {
Link link = createEntryWithLink(destination, image, text);
link.addSelectionListener(new SelectionListener() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
@Override
public void widgetSelected(SelectionEvent e) {
ActionSet actionSet = dialog.idToActionSet.get(e.text);
if (actionSet == null) {
hide();
dialog.showActionSet(item);
} else {
hide();
dialog.showActionSet(actionSet);
}
}
});
}
}
// Show key binding info
if (showKeyBindings && CustomizePerspectiveDialog.getCommandID(item) != null) {
// See if there is a command associated with the command id
ICommandService commandService = dialog.window
.getService(ICommandService.class);
Command command = commandService.getCommand(CustomizePerspectiveDialog.getCommandID(item));
if (command != null && command.isDefined()) {
// Find the bindings and list them as a string
Binding[] bindings = ItemDetailToolTip.getKeyBindings(dialog.window, item);
String keybindings = ItemDetailToolTip.keyBindingsAsString(bindings);
String text = null;
// Is it possible for this item to be visible?
final boolean available = (item.getActionSet() == null)
|| (item.getActionSet().isActive());
if (bindings.length > 0) {
if (available) {
text = NLS.bind(
WorkbenchMessages.HideItems_keyBindings,
keybindings);
} else {
text = NLS
.bind(
WorkbenchMessages.HideItems_keyBindingsActionSetUnavailable,
keybindings);
}
} else {
if (available) {
text = WorkbenchMessages.HideItems_noKeyBindings;
} else {
text = WorkbenchMessages.HideItems_noKeyBindingsActionSetUnavailable;
}
}
// Construct link to go to the preferences page for key
// bindings
final Object highlight;
if (bindings.length == 0) {
Map<String, String> parameters = new HashMap<>();
// If item is a shortcut, need to add a parameter to go
// to
// the correct item
if (item instanceof ShortcutItem) {
if (CustomizePerspectiveDialog.isNewWizard(item)) {
parameters.put(
IWorkbenchCommandConstants.FILE_NEW_PARM_WIZARDID,
CustomizePerspectiveDialog.getParamID(item));
} else if (CustomizePerspectiveDialog.isShowPerspective(item)) {
parameters
.put(
IWorkbenchCommandConstants.PERSPECTIVES_SHOW_PERSPECTIVE_PARM_ID,
CustomizePerspectiveDialog.getParamID(item));
} else if (CustomizePerspectiveDialog.isShowView(item)) {
parameters.put(
IWorkbenchCommandConstants.VIEWS_SHOW_VIEW_PARM_ID,
CustomizePerspectiveDialog.getParamID(item));
}
}
ParameterizedCommand pc = ParameterizedCommand
.generateCommand(command, parameters);
highlight = pc;
} else {
highlight = bindings[0];
}
Link bindingLink = createEntryWithLink(destination, null,
text);
bindingLink.addSelectionListener(new SelectionListener() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
@Override
public void widgetSelected(SelectionEvent e) {
PreferenceDialog pdialog = PreferencesUtil.createPreferenceDialogOn(dialog.getShell(),
CustomizePerspectiveDialog.KEYS_PREFERENCE_PAGE_ID,
new String[0], highlight);
hide();
pdialog.open();
}
});
}
}
// Show dynamic menu item info
if (item instanceof DynamicContributionItem) {
DynamicContributionItem dynamic = ((DynamicContributionItem) item);
StringBuffer text = new StringBuffer();
final List<MenuItem> currentItems = dynamic.getCurrentItems();
if (currentItems.size() > 0) {
// Create a list of the currently displayed items
text.append(WorkbenchMessages.HideItems_dynamicItemList);
for (MenuItem menuItem : currentItems) {
text.append(CustomizePerspectiveDialog.NEW_LINE).append("- ") //$NON-NLS-1$
.append(menuItem.getText());
}
} else {
text
.append(WorkbenchMessages.HideItems_dynamicItemEmptyList);
}
createEntry(destination, null, text.toString());
}
}
@Override
protected Object getModelElement(Event event) {
org.eclipse.swt.widgets.TreeItem treeItem = tree.getItem(new Point(
event.x, event.y));
if (treeItem == null) {
return null;
}
return treeItem.getData();
}
/**
* @param collection
* a collection, into which all command groups (action sets)
* which contribute <code>item</code> or its descendants will be
* placed
* @param item
* the item to collect descendants of
* @param filter
* the filter currently being used
*/
static void collectDescendantCommandGroups(Collection<ActionSet> collection,
DisplayItem item, ViewerFilter filter) {
List<TreeItem> children = item.getChildren();
for (TreeItem treeItem : children) {
DisplayItem child = (DisplayItem) treeItem;
if ((filter == null || filter.select(null, null, child))
&& child.getActionSet() != null) {
collection.add(child.getActionSet());
}
collectDescendantCommandGroups(collection, child, filter);
}
}
/**
* @param bindings
* @return a String representing the key bindings in <code>bindings</code>
*/
static String keyBindingsAsString(Binding[] bindings) {
String keybindings = null;
for (int i = 0; i < bindings.length; i++) {
// Unfortunately, bindings may be reported more than once:
// check to see if this one has already been recorded.
boolean alreadyRecorded = false;
for (int j = 0; j < i && !alreadyRecorded; j++) {
if (bindings[i].getTriggerSequence().equals(
bindings[j].getTriggerSequence())) {
alreadyRecorded = true;
}
}
if (!alreadyRecorded) {
String keybinding = bindings[i].getTriggerSequence().format();
if (i == 0) {
keybindings = keybinding;
} else {
keybindings = Util.createList(keybindings, keybinding);
}
}
}
return keybindings;
}
/**
* Gets the keybindings associated with a ContributionItem.
*/
static Binding[] getKeyBindings(WorkbenchWindow window, DisplayItem item) {
IBindingService bindingService = window.getService(IBindingService.class);
if (!(bindingService instanceof BindingService)) {
return new Binding[0];
}
String id = CustomizePerspectiveDialog.getCommandID(item);
String param = CustomizePerspectiveDialog.getParamID(item);
BindingManager bindingManager = ((BindingService) bindingService)
.getBindingManager();
Collection<?> allBindings = bindingManager
.getActiveBindingsDisregardingContextFlat();
List<Binding> foundBindings = new ArrayList<>(2);
for (Object name : allBindings) {
Binding binding = (Binding) name;
if (binding.getParameterizedCommand() == null) {
continue;
}
if (binding.getParameterizedCommand().getId() == null) {
continue;
}
if (binding.getParameterizedCommand().getId().equals(id)) {
if (param == null) {
// We found it!
foundBindings.add(binding);
} else {
// command parameters are only used in the shortcuts
Map<?, ?> m = binding.getParameterizedCommand().getParameterMap();
String key = null;
if (CustomizePerspectiveDialog.isNewWizard(item)) {
key = IWorkbenchCommandConstants.FILE_NEW_PARM_WIZARDID;
} else if (CustomizePerspectiveDialog.isShowView(item)) {
key = IWorkbenchCommandConstants.VIEWS_SHOW_VIEW_PARM_ID;
} else if (CustomizePerspectiveDialog.isShowPerspective(item)) {
key = IWorkbenchCommandConstants.PERSPECTIVES_SHOW_PERSPECTIVE_PARM_ID;
}
if (key != null) {
if (param.equals(m.get(key))) {
foundBindings.add(binding);
}
}
}
}
}
Binding[] bindings = foundBindings
.toArray(new Binding[foundBindings.size()]);
return bindings;
}
}