blob: 0c38bfb280a2835a4dd67bab54bd469a8ae1172c [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2006, 2021 IBM Corporation and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0.
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# IBM Corporation - org.eclipse.platform: initial API and implementation
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ecommons.ui.actions;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.action.IMenuListener2;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.resource.DeviceResourceException;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.help.IWorkbenchHelpSystem;
import org.eclipse.ui.menus.CommandContributionItemParameter;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.ecommons.ui.SharedUIResources;
/**
* A contribution item
*/
@NonNullByDefault
public abstract class SimpleContributionItem extends ContributionItem {
/**
* A push button tool item or menu item.
*/
public static final int STYLE_PUSH= SWT.PUSH;
/**
* A checked tool item or menu item.
*/
public static final int STYLE_CHECK= SWT.CHECK;
/**
* A radio-button style menu item.
*/
public static final int STYLE_RADIO= SWT.RADIO;
/**
* A ToolBar pulldown item.
*/
public static final int STYLE_PULLDOWN= SWT.DROP_DOWN;
/**
* Mode bit: Show text on tool items or buttons, even if an image is
* present. If this mode bit is not set, text is only shown on tool items if
* there is no image present.
*
* @since 3.4
*/
public static final int MODE_FORCE_TEXT= 1;
private LocalResourceManager localResourceManager;
private @Nullable Widget widget;
private @Nullable Listener menuItemListener;
private @Nullable String label;
private @Nullable String mnemonic;
private @Nullable String tooltip;
private @Nullable ImageDescriptor icon;
private @Nullable ImageDescriptor disabledIcon;
private @Nullable ImageDescriptor hoverIcon;
private boolean checkedState;
private final int style;
private @Nullable IWorkbenchHelpSystem workbenchHelpSystem;
private @Nullable String helpContextId;
private final int mode= 0;
/**
* Create a CommandContributionItem to place in a ContributionManager.
*
* @param contributionParameters
* paramters necessary to render this contribution item.
*/
public SimpleContributionItem(
final CommandContributionItemParameter contributionParameters) {
super(contributionParameters.id);
this.label= contributionParameters.label;
this.mnemonic= contributionParameters.mnemonic;
this.tooltip= contributionParameters.tooltip;
this.icon= contributionParameters.icon;
this.disabledIcon= contributionParameters.disabledIcon;
this.hoverIcon= contributionParameters.hoverIcon;
this.style= contributionParameters.style;
this.helpContextId= contributionParameters.helpContextId;
}
protected SimpleContributionItem(final @Nullable String id,
final @Nullable String label, final @Nullable String mnemonic,
final @Nullable ImageDescriptor icon, final @Nullable ImageDescriptor disabledIcon,
final int style) {
super(id);
this.label= label;
this.mnemonic= mnemonic;
this.icon= icon;
this.disabledIcon= disabledIcon;
this.style= style;
}
protected SimpleContributionItem(final @Nullable String label, final @Nullable String mnemonic,
final @Nullable ImageDescriptor icon, final @Nullable ImageDescriptor disabledIcon,
final int style) {
this(null, label, mnemonic, icon, disabledIcon, style);
}
protected SimpleContributionItem(final @Nullable String label, final @Nullable String mnemonic,
final int style) {
this(null, label, mnemonic, null, null, style);
}
protected SimpleContributionItem(final @Nullable String label, final @Nullable String mnemonic,
final @Nullable ImageDescriptor icon, final @Nullable ImageDescriptor disabledIcon) {
this(null, label, mnemonic, icon, disabledIcon, STYLE_PUSH);
}
protected SimpleContributionItem(
final @Nullable String label, final @Nullable String mnemonic) {
this(null, label, mnemonic, null, null, STYLE_PUSH);
}
@Override
public void fill(final Menu parent, final int index) {
if (this.widget != null || parent == null) {
return;
}
// Menus don't support the pulldown style
int tmpStyle= this.style;
if (tmpStyle == STYLE_PULLDOWN) {
tmpStyle= STYLE_PUSH;
}
MenuItem item= null;
if (index >= 0) {
item= new MenuItem(parent, tmpStyle, index);
}
else {
item= new MenuItem(parent, tmpStyle);
}
item.setData(this);
if (this.workbenchHelpSystem != null) {
this.workbenchHelpSystem.setHelp(item, this.helpContextId);
}
item.addListener(SWT.Dispose, getItemListener());
item.addListener(SWT.Selection, getItemListener());
this.widget= item;
update(null);
updateIcons();
}
@Override
public void fill(final ToolBar parent, final int index) {
if (this.widget != null || parent == null) {
return;
}
ToolItem item= null;
if (index >= 0) {
item= new ToolItem(parent, this.style, index);
}
else {
item= new ToolItem(parent, this.style);
}
item.setData(this);
item.addListener(SWT.Selection, getItemListener());
item.addListener(SWT.Dispose, getItemListener());
this.widget= item;
update(null);
updateIcons();
}
@Override
public void fill(final Composite parent) {
if (this.widget != null || parent == null) {
return;
}
// Buttons don't support the pulldown style
int tmpStyle= this.style;
if (tmpStyle == STYLE_PULLDOWN) {
tmpStyle= STYLE_PUSH;
}
final Button item= new Button(parent, tmpStyle);
item.setData(this);
if (this.workbenchHelpSystem != null) {
this.workbenchHelpSystem.setHelp(item, this.helpContextId);
}
item.addListener(SWT.Dispose, getItemListener());
item.addListener(SWT.Selection, getItemListener());
this.widget= item;
update(null);
updateIcons();
}
@Override
public void update() {
update(null);
}
@Override
public void update(final String id) {
if (this.widget != null) {
if (this.widget instanceof MenuItem) {
updateMenuItem((MenuItem) this.widget);
}
else if (this.widget instanceof ToolItem) {
updateToolItem((ToolItem) this.widget);
}
else if (this.widget instanceof Button) {
updateButton((Button) this.widget);
}
}
}
private void updateMenuItem(final MenuItem item) {
final boolean shouldBeEnabled= isEnabled();
// disabled command + visibility follows enablement == disposed
if (item.isDisposed()) {
return;
}
String text= this.label;
text= updateMnemonic(text);
final String keyBindingText= null;
if (text != null) {
if (keyBindingText == null) {
item.setText(text);
}
else {
item.setText(text + '\t' + keyBindingText);
}
}
if (item.getSelection() != this.checkedState) {
item.setSelection(this.checkedState);
}
if (item.getEnabled() != shouldBeEnabled) {
item.setEnabled(shouldBeEnabled);
}
}
private void updateToolItem(final ToolItem item) {
final boolean shouldBeEnabled= isEnabled();
// disabled command + visibility follows enablement == disposed
if (item.isDisposed()) {
return;
}
final String text= this.label;
if ((this.icon == null || (this.mode & MODE_FORCE_TEXT) == MODE_FORCE_TEXT)
&& text != null) {
item.setText(text);
}
final String toolTipText= getToolTipText(text);
item.setToolTipText(toolTipText);
if (item.getSelection() != this.checkedState) {
item.setSelection(this.checkedState);
}
if (item.getEnabled() != shouldBeEnabled) {
item.setEnabled(shouldBeEnabled);
}
}
private void updateButton(final Button item) {
final boolean shouldBeEnabled= isEnabled();
// disabled command + visibility follows enablement == disposed
if (item.isDisposed()) {
return;
}
final String text= this.label;
if (text != null) {
item.setText(text);
}
final String toolTipText= getToolTipText(text);
item.setToolTipText(toolTipText);
if (item.getSelection() != this.checkedState) {
item.setSelection(this.checkedState);
}
if (item.getEnabled() != shouldBeEnabled) {
item.setEnabled(shouldBeEnabled);
}
}
private String getToolTipText(final String text) {
String tooltipText= this.tooltip;
if (this.tooltip == null) {
if (text != null) {
tooltipText= text;
}
else {
tooltipText= ""; //$NON-NLS-1$
}
}
return tooltipText;
}
private String updateMnemonic(final String s) {
if (this.mnemonic == null || s == null) {
return s;
}
final int idx= s.indexOf(this.mnemonic);
if (idx == -1) {
return s;
}
return s.substring(0, idx) + '&' + s.substring(idx);
}
private void handleWidgetDispose(final Event event) {
if (event.widget == this.widget) {
this.widget.removeListener(SWT.Selection, getItemListener());
this.widget.removeListener(SWT.Dispose, getItemListener());
this.widget= null;
disposeOldImages();
}
}
@Override
public void dispose() {
if (this.widget != null) {
this.widget.dispose();
this.widget= null;
}
disposeOldImages();
super.dispose();
}
private void disposeOldImages() {
if (this.localResourceManager != null) {
this.localResourceManager.dispose();
this.localResourceManager= null;
}
}
private Listener getItemListener() {
if (this.menuItemListener == null) {
this.menuItemListener= new Listener() {
@Override
public void handleEvent(final Event event) {
switch (event.type) {
case SWT.Dispose:
handleWidgetDispose(event);
break;
case SWT.Selection:
if (event.widget != null) {
handleWidgetSelection(event);
}
break;
}
}
};
}
return this.menuItemListener;
}
private void handleWidgetSelection(final Event event) {
// Special check for ToolBar dropdowns...
if (openDropDownMenu(event)) {
return;
}
if ((this.style & (SWT.TOGGLE | SWT.CHECK)) != 0) {
if (event.widget instanceof ToolItem) {
this.checkedState= ((ToolItem) event.widget).getSelection();
}
else if (event.widget instanceof MenuItem) {
this.checkedState= ((MenuItem) event.widget).getSelection();
}
}
try {
execute(event);
} catch (final ExecutionException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, SharedUIResources.BUNDLE_ID,
"Failed to execute item " + getId(), e)); //$NON-NLS-1$
}
}
/**
* Determines if the selection was on the dropdown affordance and, if so,
* opens the drop down menu (populated using the same id as this item...
*
* @param event
* The <code>SWT.Selection</code> event to be tested
*
* @return <code>true</code> iff a drop down menu was opened
*/
private boolean openDropDownMenu(final Event event) {
final Widget item= event.widget;
if (item != null) {
final int style= item.getStyle();
if ((style & SWT.DROP_DOWN) != 0) {
if (event.detail == 4) { // on drop-down button
final ToolItem ti= (ToolItem) item;
final MenuManager menuManager= new MenuManager();
final Menu menu= menuManager.createContextMenu(ti.getParent());
if (this.workbenchHelpSystem != null) {
this.workbenchHelpSystem.setHelp(menu, this.helpContextId);
}
initDropDownMenu(menuManager);
// position the menu below the drop down item
final Point point= ti.getParent().toDisplay(
new Point(event.x, event.y));
menu.setLocation(point.x, point.y); // waiting for SWT
// 0.42
menu.setVisible(true);
return true; // we don't fire the action
}
}
}
return false;
}
protected void initDropDownMenu(final MenuManager menuManager) {
final Menu menu= menuManager.getMenu();
menuManager.addMenuListener(new IMenuListener2() {
@Override
public void menuAboutToShow(final IMenuManager manager) {
dropDownMenuAboutToShow(manager);
}
@Override
public void menuAboutToHide(final IMenuManager manager) {
menu.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
menuManager.dispose();
}
});
}
});
}
private void updateIcons() {
if (this.widget instanceof MenuItem) {
final MenuItem item= (MenuItem) this.widget;
final LocalResourceManager m= new LocalResourceManager(JFaceResources
.getResources());
try {
item.setImage(this.icon == null ? null : m.createImage(this.icon));
} catch (final DeviceResourceException e) {
this.icon= ImageDescriptor.getMissingImageDescriptor();
item.setImage(m.createImage(this.icon));
// as we replaced the failed icon, log the message once.
StatusManager.getManager().handle(new Status(IStatus.ERROR, SharedUIResources.BUNDLE_ID,
"Failed to load image", e)); //$NON-NLS-1$
}
disposeOldImages();
this.localResourceManager= m;
}
else if (this.widget instanceof ToolItem) {
final ToolItem item= (ToolItem) this.widget;
final LocalResourceManager m= new LocalResourceManager(JFaceResources
.getResources());
item.setDisabledImage(this.disabledIcon == null ? null : m
.createImage(this.disabledIcon));
item.setHotImage(this.hoverIcon == null ? null : m
.createImage(this.hoverIcon));
item.setImage(this.icon == null ? null : m.createImage(this.icon));
disposeOldImages();
this.localResourceManager= m;
}
}
public void setText(final @Nullable String text) {
this.label= text;
update(null);
}
public void setChecked(final boolean checked) {
if (this.checkedState == checked) {
return;
}
this.checkedState= checked;
if (this.widget instanceof MenuItem) {
((MenuItem) this.widget).setSelection(this.checkedState);
}
else if (this.widget instanceof ToolItem) {
((ToolItem) this.widget).setSelection(this.checkedState);
}
}
public void setTooltip(final @Nullable String text) {
this.tooltip= text;
if (this.widget instanceof ToolItem) {
((ToolItem) this.widget).setToolTipText(text);
}
}
public void setIcon(final @Nullable ImageDescriptor desc) {
this.icon= desc;
updateIcons();
}
public void setDisabledIcon(final @Nullable ImageDescriptor desc) {
this.disabledIcon= desc;
updateIcons();
}
public void setHoverIcon(final @Nullable ImageDescriptor desc) {
this.hoverIcon= desc;
updateIcons();
}
protected void dropDownMenuAboutToShow(final IMenuManager manager) {
}
protected void execute(final Event event) throws ExecutionException {
execute();
}
@Deprecated
protected void execute() throws ExecutionException {
}
}