/*******************************************************************************
* Copyright (c) 2010, 2013 SAP AG 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:
* Mathias Kinzler (SAP AG) - initial implementation
*******************************************************************************/
package org.eclipse.egit.ui;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.NotEnabledException;
import org.eclipse.core.commands.NotHandledException;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.variables.IStringVariableManager;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.egit.ui.internal.CommonUtils;
import org.eclipse.egit.ui.internal.RepositorySaveableFilter;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.components.RefContentProposal;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.bindings.Trigger;
import org.eclipse.jface.bindings.TriggerSequence;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.fieldassist.IContentProposalProvider;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Resource;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
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.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.ISources;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ContributionItemFactory;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.services.IServiceLocator;
/**
* Some utilities for UI code
*/
public class UIUtils {
/**
* these activate the content assist; alphanumeric, space plus some expected
* special chars
*/
private static final char[] VALUE_HELP_ACTIVATIONCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123457890*@ <>".toCharArray(); //$NON-NLS-1$
/**
* A keystroke for a "submit" action, see {@link #isSubmitKeyEvent(KeyEvent)}
*/
public static final KeyStroke SUBMIT_KEY_STROKE = KeyStroke.getInstance(SWT.MOD1, SWT.CR);
/**
* Handles a "previously used values" content assist.
* <p>
* Adding this to a text field will enable "content assist" by keeping track
* of the previously used valued for this field. The previously used values
* will be shown in the order they were last used (most recently used ones
* coming first in the list) and the number of entries is limited.
* <p>
* A "bulb" decorator will indicate that content assist is available for the
* field, and a tool tip is provided giving more information.
* <p>
* Content assist is activated by either typing in the field or by using a
* dedicated key stroke which is indicated in the tool tip. The list will be
* filtered with the content already in the text field with '*' being usable
* as wild card.
* <p>
* Note that the application must issue a call to {@link #updateProposals()}
* in order to add a new value to the "previously used values" list.
* <p>
* The list will be persisted in the plug-in dialog settings.
*
* @noextend not to be extended by clients
* @noimplement not to be implemented by clients, use
* {@link UIUtils#addPreviousValuesContentProposalToText(Text, String)}
* to create instances of this
*/
public interface IPreviousValueProposalHandler {
/**
* Updates the proposal list from the value in the text field.
* <p>
* The value will be truncated to the first 2000 characters in order to
* limit data size.
* <p>
* Note that this must be called in the UI thread, since it accesses the
* text field.
* <p>
* If the value is already in the list, it will become the first entry,
* otherwise it will be added at the beginning. Note that empty Strings
* will not be stored. The length of the list is limited, and the
* "oldest" entries will be removed once the limit is exceeded.
* <p>
* This call should only be issued if the value in the text field is
* "valid" in terms of the application.
*/
public void updateProposals();
}
/**
* Used for
* {@link UIUtils#addRefContentProposalToText(Text, Repository, IRefListProvider)}
*/
public interface IRefListProvider {
/**
* @return the List of {@link Ref}s to propose
*/
public List<Ref> getRefList();
}
/**
* @param id
* see {@link FontRegistry#get(String)}
* @return the font
*/
public static Font getFont(final String id) {
return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme()
.getFontRegistry().get(id);
}
/**
* @param id
* see {@link FontRegistry#getBold(String)}
* @return the font
*/
public static Font getBoldFont(final String id) {
return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme()
.getFontRegistry().getBold(id);
}
/**
* @param id
* see {@link FontRegistry#getItalic(String)}
* @return the font
*/
public static Font getItalicFont(final String id) {
return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme()
.getFontRegistry().getItalic(id);
}
/**
* @return the indent of controls that depend on the previous control (e.g.
* a checkbox that is only enabled when the checkbox above it is
* checked)
*/
public static int getControlIndent() {
// Eclipse 4.3: Use LayoutConstants.getIndent once we depend on 4.3
return 20;
}
/**
* @param parent
* @param style
* @return a text field which is read-only but can be selected
*/
public static Text createSelectableLabel(Composite parent, int style) {
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=71765
Text text = new Text(parent, style | SWT.READ_ONLY);
text.setBackground(text.getDisplay().getSystemColor(
SWT.COLOR_WIDGET_BACKGROUND));
return text;
}
/**
* Adds little bulb decoration to given control. Bulb will appear in top
* left corner of control after giving focus for this control.
*
* After clicking on bulb image text from <code>tooltip</code> will appear.
*
* @param control
* instance of {@link Control} object with should be decorated
* @param tooltip
* text value which should appear after clicking on bulb image.
*/
public static void addBulbDecorator(final Control control,
final String tooltip) {
ControlDecoration dec = new ControlDecoration(control, SWT.TOP
| SWT.LEFT);
dec.setImage(FieldDecorationRegistry.getDefault().getFieldDecoration(
FieldDecorationRegistry.DEC_CONTENT_PROPOSAL).getImage());
dec.setShowOnlyOnFocus(true);
dec.setShowHover(true);
dec.setDescriptionText(tooltip);
}
/**
* Adds a "previously used values" content proposal handler to a text field.
* <p>
* The list will be limited to 10 values.
*
* @param textField
* the text field
* @param preferenceKey
* the key under which to store the "previously used values" in
* the dialog settings
* @return the handler the proposal handler
*/
public static IPreviousValueProposalHandler addPreviousValuesContentProposalToText(
final Text textField, final String preferenceKey) {
KeyStroke stroke = UIUtils
.getKeystrokeOfBestActiveBindingFor(IWorkbenchCommandConstants.EDIT_CONTENT_ASSIST);
if (stroke == null)
addBulbDecorator(textField,
UIText.UIUtils_StartTypingForPreviousValuesMessage);
else
addBulbDecorator(
textField,
NLS.bind(UIText.UIUtils_PressShortcutMessage,
stroke.format()));
IContentProposalProvider cp = new IContentProposalProvider() {
public IContentProposal[] getProposals(String contents, int position) {
List<IContentProposal> resultList = new ArrayList<IContentProposal>();
// make the simplest possible pattern check: allow "*"
// for multiple characters
String patternString = contents;
// ignore spaces in the beginning
while (patternString.length() > 0
&& patternString.charAt(0) == ' ') {
patternString = patternString.substring(1);
}
// we quote the string as it may contain spaces
// and other stuff colliding with the Pattern
patternString = Pattern.quote(patternString);
patternString = patternString.replaceAll("\\x2A", ".*"); //$NON-NLS-1$ //$NON-NLS-2$
// make sure we add a (logical) * at the end
if (!patternString.endsWith(".*")) { //$NON-NLS-1$
patternString = patternString + ".*"; //$NON-NLS-1$
}
// let's compile a case-insensitive pattern (assumes ASCII only)
Pattern pattern;
try {
pattern = Pattern.compile(patternString,
Pattern.CASE_INSENSITIVE);
} catch (PatternSyntaxException e) {
pattern = null;
}
String[] proposals = org.eclipse.egit.ui.Activator.getDefault()
.getDialogSettings().getArray(preferenceKey);
if (proposals != null)
for (final String uriString : proposals) {
if (pattern != null
&& !pattern.matcher(uriString).matches())
continue;
IContentProposal propsal = new IContentProposal() {
public String getLabel() {
return null;
}
public String getDescription() {
return null;
}
public int getCursorPosition() {
return 0;
}
public String getContent() {
return uriString;
}
};
resultList.add(propsal);
}
return resultList.toArray(new IContentProposal[resultList
.size()]);
}
};
ContentProposalAdapter adapter = new ContentProposalAdapter(textField,
new TextContentAdapter(), cp, stroke,
VALUE_HELP_ACTIVATIONCHARS);
// set the acceptance style to always replace the complete content
adapter
.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
return new IPreviousValueProposalHandler() {
public void updateProposals() {
String value = textField.getText();
// don't store empty values
if (value.length() > 0) {
// we don't want to save too much in the preferences
if (value.length() > 2000) {
value = value.substring(0, 1999);
}
// now we need to mix the value into the list
IDialogSettings settings = org.eclipse.egit.ui.Activator
.getDefault().getDialogSettings();
String[] existingValues = settings.getArray(preferenceKey);
if (existingValues == null) {
existingValues = new String[] { value };
settings.put(preferenceKey, existingValues);
} else {
List<String> values = new ArrayList<String>(
existingValues.length + 1);
for (String existingValue : existingValues)
values.add(existingValue);
// if it is already the first value, we don't need to do
// anything
if (values.indexOf(value) == 0)
return;
values.remove(value);
// we insert at the top
values.add(0, value);
// make sure to not store more than the maximum number
// of values
while (values.size() > 10)
values.remove(values.size() - 1);
settings.put(preferenceKey, values
.toArray(new String[values.size()]));
}
}
}
};
}
/**
* Adds a content proposal for {@link Ref}s (branches, tags...) to a text
* field
*
* @param textField
* the text field
* @param repository
* the repository
* @param refListProvider
* provides the {@link Ref}s to show in the proposal
*/
public static final void addRefContentProposalToText(final Text textField,
final Repository repository, final IRefListProvider refListProvider) {
KeyStroke stroke = UIUtils
.getKeystrokeOfBestActiveBindingFor(IWorkbenchCommandConstants.EDIT_CONTENT_ASSIST);
if (stroke == null)
addBulbDecorator(textField,
UIText.UIUtils_StartTypingForPreviousValuesMessage);
else
addBulbDecorator(
textField,
NLS.bind(UIText.UIUtils_PressShortcutMessage,
stroke.format()));
IContentProposalProvider cp = new IContentProposalProvider() {
public IContentProposal[] getProposals(String contents, int position) {
List<IContentProposal> resultList = new ArrayList<IContentProposal>();
// make the simplest possible pattern check: allow "*"
// for multiple characters
String patternString = contents;
// ignore spaces in the beginning
while (patternString.length() > 0
&& patternString.charAt(0) == ' ') {
patternString = patternString.substring(1);
}
// we quote the string as it may contain spaces
// and other stuff colliding with the Pattern
patternString = Pattern.quote(patternString);
patternString = patternString.replaceAll("\\x2A", ".*"); //$NON-NLS-1$ //$NON-NLS-2$
// make sure we add a (logical) * at the end
if (!patternString.endsWith(".*")) { //$NON-NLS-1$
patternString = patternString + ".*"; //$NON-NLS-1$
}
// let's compile a case-insensitive pattern (assumes ASCII only)
Pattern pattern;
try {
pattern = Pattern.compile(patternString,
Pattern.CASE_INSENSITIVE);
} catch (PatternSyntaxException e) {
pattern = null;
}
List<Ref> proposals = refListProvider.getRefList();
if (proposals != null)
for (final Ref ref : proposals) {
final String shortenedName = Repository
.shortenRefName(ref.getName());
if (pattern != null
&& !pattern.matcher(ref.getName()).matches()
&& !pattern.matcher(shortenedName).matches())
continue;
IContentProposal propsal = new RefContentProposal(
repository, ref);
resultList.add(propsal);
}
return resultList.toArray(new IContentProposal[resultList
.size()]);
}
};
ContentProposalAdapter adapter = new ContentProposalAdapter(textField,
new TextContentAdapter(), cp, stroke,
UIUtils.VALUE_HELP_ACTIVATIONCHARS);
// set the acceptance style to always replace the complete content
adapter
.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
}
/**
* Set enabled state of the control and all its children
* @param control
* @param enable
*/
public static void setEnabledRecursively(final Control control,
final boolean enable) {
control.setEnabled(enable);
if (control instanceof Composite)
for (final Control child : ((Composite) control).getChildren())
setEnabledRecursively(child, enable);
}
/**
* Dispose of the resource when the widget is disposed
*
* @param widget
* @param resource
*/
public static void hookDisposal(Widget widget, final Resource resource) {
if (widget == null || resource == null)
return;
widget.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
resource.dispose();
}
});
}
/**
* Dispose of the resource manager when the widget is disposed
*
* @param widget
* @param resources
*/
public static void hookDisposal(Widget widget,
final ResourceManager resources) {
if (widget == null || resources == null)
return;
widget.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
resources.dispose();
}
});
}
/**
* Get editor image for path
*
* @param path
* @return image descriptor
*/
public static ImageDescriptor getEditorImage(final String path) {
if (path != null && path.length() > 0) {
final String name = new Path(path).lastSegment();
if (name != null)
return PlatformUI.getWorkbench().getEditorRegistry()
.getImageDescriptor(name);
}
return PlatformUI.getWorkbench().getSharedImages()
.getImageDescriptor(ISharedImages.IMG_OBJ_FILE);
}
/**
* Get size of image descriptor as point.
*
* @param descriptor
* @return size
*/
public static Point getSize(ImageDescriptor descriptor) {
ImageData data = descriptor.getImageData();
if (data == null)
return new Point(0, 0);
return new Point(data.width, data.height);
}
/**
* Add expand all and collapse all toolbar items to the given toolbar bound
* to the given tree viewer
*
* @param toolbar
* @param viewer
* @return given toolbar
*/
public static ToolBar addExpansionItems(final ToolBar toolbar,
final AbstractTreeViewer viewer) {
ToolItem collapseItem = new ToolItem(toolbar, SWT.PUSH);
Image collapseImage = UIIcons.COLLAPSEALL.createImage();
UIUtils.hookDisposal(collapseItem, collapseImage);
collapseItem.setImage(collapseImage);
collapseItem.setToolTipText(UIText.UIUtils_CollapseAll);
collapseItem.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
viewer.collapseAll();
}
});
ToolItem expandItem = new ToolItem(toolbar, SWT.PUSH);
Image expandImage = UIIcons.EXPAND_ALL.createImage();
UIUtils.hookDisposal(expandItem, expandImage);
expandItem.setImage(expandImage);
expandItem.setToolTipText(UIText.UIUtils_ExpandAll);
expandItem.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
viewer.expandAll();
}
});
return toolbar;
}
/**
* Get dialog bound settings for given class using standard section name
*
* @param clazz
* @return dialog setting
*/
public static IDialogSettings getDialogBoundSettings(final Class<?> clazz) {
return getDialogSettings(clazz.getName() + ".dialogBounds"); //$NON-NLS-1$
}
/**
* Get dialog settings for given section name
*
* @param sectionName
* @return dialog settings
*/
public static IDialogSettings getDialogSettings(final String sectionName) {
IDialogSettings settings = Activator.getDefault().getDialogSettings();
IDialogSettings section = settings.getSection(sectionName);
if (section == null)
section = settings.addNewSection(sectionName);
return section;
}
/**
* @return The default repository directory as configured in the
* preferences, with variables substituted. An empty string if there
* was an error during substitution.
*/
public static String getDefaultRepositoryDir() {
String dir = Activator.getDefault().getPreferenceStore()
.getString(UIPreferences.DEFAULT_REPO_DIR);
IStringVariableManager manager = VariablesPlugin.getDefault()
.getStringVariableManager();
try {
return manager.performStringSubstitution(dir);
} catch (CoreException e) {
return ""; //$NON-NLS-1$
}
}
/**
* Is viewer in a usable state?
*
* @param viewer
* @return true if usable, false if null or underlying control is null or
* disposed
*/
public static boolean isUsable(final Viewer viewer) {
return viewer != null && isUsable(viewer.getControl());
}
/**
* Is control usable?
*
* @param control
* @return true if usable, false if null or disposed
*/
public static boolean isUsable(final Control control) {
return control != null && !control.isDisposed();
}
/**
* Run command with specified id
*
* @param service
* @param id
*/
public static void executeCommand(IHandlerService service, String id) {
executeCommand(service, id, null);
}
/**
* Run command with specified id
*
* @param service
* @param id
* @param event
*/
public static void executeCommand(IHandlerService service, String id,
Event event) {
try {
service.executeCommand(id, event);
} catch (ExecutionException e) {
Activator.handleError(e.getMessage(), e, false);
} catch (NotDefinedException e) {
Activator.handleError(e.getMessage(), e, false);
} catch (NotEnabledException e) {
Activator.handleError(e.getMessage(), e, false);
} catch (NotHandledException e) {
Activator.handleError(e.getMessage(), e, false);
}
}
/**
* Determine if the key event represents a "submit" action
* (<modifier>+Enter).
*
* @param event
* @return true, if it means submit, false otherwise
*/
public static boolean isSubmitKeyEvent(KeyEvent event) {
return (event.stateMask & SWT.MODIFIER_MASK) != 0
&& event.keyCode == SUBMIT_KEY_STROKE.getNaturalKey();
}
/**
* Prompt for saving all dirty editors for resources in the working
* directory of the specified repository.
*
* @param repository
* @return true, if the user opted to continue, false otherwise
* @see IWorkbench#saveAllEditors(boolean)
*/
public static boolean saveAllEditors(Repository repository) {
return saveAllEditors(repository, null);
}
/**
* Prompt for saving all dirty editors for resources in the working
* directory of the specified repository.
*
* If at least one file was saved, a dialog is displayed, asking the user if
* she wants to cancel the operation. Cancelling allows the user to do
* something with the newly saved files, before possibly restarting the
* operation.
*
* @param repository
* @param cancelConfirmationQuestion
* A string asking the user if she wants to cancel the operation.
* May be null to not open a dialog, but rather always continue.
* @return true, if the user opted to continue, false otherwise
* @see IWorkbench#saveAllEditors(boolean)
*/
public static boolean saveAllEditors(Repository repository,
String cancelConfirmationQuestion) {
IWorkbench workbench = PlatformUI.getWorkbench();
IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
RepositorySaveableFilter filter = new RepositorySaveableFilter(
repository);
boolean success = workbench.saveAll(window, window, filter, true);
if (success && cancelConfirmationQuestion != null && filter.isAnythingSaved()){
// allow the user to cancel the operation to first do something with
// the newly saved files
String[] buttons = new String[] { IDialogConstants.YES_LABEL,
IDialogConstants.NO_LABEL };
MessageDialog dialog = new MessageDialog(window.getShell(),
UIText.CancelAfterSaveDialog_Title, null,
cancelConfirmationQuestion,
MessageDialog.QUESTION, buttons, 0) {
protected int getShellStyle() {
return (SWT.TITLE | SWT.BORDER | SWT.APPLICATION_MODAL
| SWT.SHEET | getDefaultOrientation());
}
};
int choice = dialog.open();
if (choice != 1) // user clicked "yes" or closed dialog -> cancel
return false;
}
return success;
}
/**
* @param workbenchWindow the workbench window to use for creating the show in menu.
* @return the show in menu
*/
public static MenuManager createShowInMenu(IWorkbenchWindow workbenchWindow) {
MenuManager showInSubMenu = new MenuManager(getShowInMenuLabel());
showInSubMenu.add(ContributionItemFactory.VIEWS_SHOW_IN.create(workbenchWindow));
return showInSubMenu;
}
/**
* Use hyperlink detectors to find a text viewer's hyperlinks and return the
* style ranges to render them.
*
* @param textViewer
* @param hyperlinkDetectors
* @return the style ranges to render the detected hyperlinks
*/
public static StyleRange[] getHyperlinkDetectorStyleRanges(
ITextViewer textViewer, IHyperlinkDetector[] hyperlinkDetectors) {
HashSet<StyleRange> styleRangeList = new HashSet<StyleRange>();
if (hyperlinkDetectors != null && hyperlinkDetectors.length > 0) {
IDocument doc = textViewer.getDocument();
for (int line = 0; line < doc.getNumberOfLines(); line++) {
try {
IRegion region = doc.getLineInformation(line);
for (IHyperlinkDetector hyperLinkDetector : hyperlinkDetectors) {
IHyperlink[] hyperlinks = hyperLinkDetector
.detectHyperlinks(textViewer, region, true);
if (hyperlinks != null) {
for (IHyperlink hyperlink : hyperlinks) {
StyleRange hyperlinkStyleRange = new StyleRange(
hyperlink.getHyperlinkRegion()
.getOffset(), hyperlink
.getHyperlinkRegion()
.getLength(), Display
.getDefault().getSystemColor(
SWT.COLOR_BLUE),
Display.getDefault().getSystemColor(
SWT.COLOR_WHITE));
hyperlinkStyleRange.underline = true;
styleRangeList.add(hyperlinkStyleRange);
}
}
}
} catch (BadLocationException e) {
Activator.logError(e.getMessage(), e);
break;
}
}
}
StyleRange[] styleRangeArray = new StyleRange[styleRangeList.size()];
styleRangeList.toArray(styleRangeArray);
return styleRangeArray;
}
private static String getShowInMenuLabel() {
IBindingService bindingService = (IBindingService) PlatformUI
.getWorkbench().getAdapter(IBindingService.class);
if (bindingService != null) {
String keyBinding = bindingService
.getBestActiveBindingFormattedFor(IWorkbenchCommandConstants.NAVIGATE_SHOW_IN_QUICK_MENU);
if (keyBinding != null)
return UIText.UIUtils_ShowInMenuLabel + '\t' + keyBinding;
}
return UIText.UIUtils_ShowInMenuLabel;
}
/**
* Look up best active binding's keystroke for the given command
*
* @param commandId
* The identifier of the command for which the best active
* binding's keystroke should be retrieved; must not be null.
* @return {@code KeyStroke} for the best active binding for the specified
* commandId or {@code null} if no binding is defined or if the
* binding service returns a {@code TriggerSequence} containing more
* than one {@code Trigger}.
*/
public static KeyStroke getKeystrokeOfBestActiveBindingFor(String commandId) {
IBindingService bindingService = (IBindingService) PlatformUI
.getWorkbench().getAdapter(IBindingService.class);
TriggerSequence ts = bindingService.getBestActiveBindingFor(commandId);
if (ts == null)
return null;
Trigger[] triggers = ts.getTriggers();
if (triggers.length == 1 && triggers[0] instanceof KeyStroke)
return (KeyStroke) triggers[0];
else
return null;
}
/**
* Copy from {@link org.eclipse.jface.dialogs.DialogPage} with changes to
* accommodate the lack of a Dialog context.
*
* @param button
* the button to set the <code>GridData</code>
*/
public static void setButtonLayoutData(Button button) {
GC gc = new GC(button);
gc.setFont(JFaceResources.getDialogFont());
FontMetrics fontMetrics = gc.getFontMetrics();
gc.dispose();
GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
int widthHint = Dialog.convertHorizontalDLUsToPixels(fontMetrics,
IDialogConstants.BUTTON_WIDTH);
Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
data.widthHint = Math.max(widthHint, minSize.x);
button.setLayoutData(data);
}
/**
* Locates the current part and selection and fires
* {@link ISelectionListener#selectionChanged(IWorkbenchPart, ISelection)}
* on the passed listener.
*
* @param serviceLocator
* @param selectionListener
*/
public static void notifySelectionChangedWithCurrentSelection(
ISelectionListener selectionListener, IServiceLocator serviceLocator) {
IHandlerService handlerService = CommonUtils.getService(serviceLocator, IHandlerService.class);
IEvaluationContext state = handlerService.getCurrentState();
// This seems to be the most reliable way to get the active part, it
// also returns a part when it is called while creating a view that is
// being shown.Getting the active part through the active workbench
// window returned null in that case.
Object partObject = state.getVariable(ISources.ACTIVE_PART_NAME);
Object selectionObject = state
.getVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME);
if (partObject instanceof IWorkbenchPart
&& selectionObject instanceof ISelection) {
IWorkbenchPart part = (IWorkbenchPart) partObject;
ISelection selection = (ISelection) selectionObject;
if (!selection.isEmpty())
selectionListener.selectionChanged(part, selection);
}
}
}