Update org.apache.commons:commons-compress to 1.25.0
[egit/eclipse.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / UIUtils.java
blobdcb33ba43ffe95750da6ed57e75fbb0751a1c24c
1 /*******************************************************************************
2 * Copyright (c) 2010, 2018 SAP AG and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License 2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-2.0/
8 * SPDX-License-Identifier: EPL-2.0
10 * Contributors:
11 * Mathias Kinzler (SAP AG) - initial implementation
12 *******************************************************************************/
13 package org.eclipse.egit.ui;
15 import java.lang.ref.SoftReference;
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.Collection;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.function.Function;
23 import java.util.regex.Pattern;
24 import java.util.regex.PatternSyntaxException;
26 import org.eclipse.core.commands.ExecutionException;
27 import org.eclipse.core.commands.NotEnabledException;
28 import org.eclipse.core.commands.NotHandledException;
29 import org.eclipse.core.commands.common.NotDefinedException;
30 import org.eclipse.core.expressions.IEvaluationContext;
31 import org.eclipse.core.runtime.Adapters;
32 import org.eclipse.core.runtime.Path;
33 import org.eclipse.egit.core.internal.Utils;
34 import org.eclipse.egit.ui.internal.RepositorySaveableFilter;
35 import org.eclipse.egit.ui.internal.UIIcons;
36 import org.eclipse.egit.ui.internal.UIText;
37 import org.eclipse.egit.ui.internal.components.RefContentProposal;
38 import org.eclipse.jface.action.MenuManager;
39 import org.eclipse.jface.bindings.Trigger;
40 import org.eclipse.jface.bindings.TriggerSequence;
41 import org.eclipse.jface.bindings.keys.KeyStroke;
42 import org.eclipse.jface.dialogs.Dialog;
43 import org.eclipse.jface.dialogs.IDialogConstants;
44 import org.eclipse.jface.dialogs.IDialogSettings;
45 import org.eclipse.jface.dialogs.MessageDialog;
46 import org.eclipse.jface.fieldassist.ContentProposalAdapter;
47 import org.eclipse.jface.fieldassist.ControlDecoration;
48 import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
49 import org.eclipse.jface.fieldassist.IContentProposal;
50 import org.eclipse.jface.fieldassist.IContentProposalProvider;
51 import org.eclipse.jface.fieldassist.IControlContentAdapter;
52 import org.eclipse.jface.fieldassist.TextContentAdapter;
53 import org.eclipse.jface.resource.FontRegistry;
54 import org.eclipse.jface.resource.ImageDescriptor;
55 import org.eclipse.jface.resource.JFaceResources;
56 import org.eclipse.jface.resource.ResourceManager;
57 import org.eclipse.jface.viewers.AbstractTreeViewer;
58 import org.eclipse.jface.viewers.ISelection;
59 import org.eclipse.jface.viewers.StructuredSelection;
60 import org.eclipse.jface.viewers.Viewer;
61 import org.eclipse.jgit.annotations.NonNull;
62 import org.eclipse.jgit.annotations.Nullable;
63 import org.eclipse.jgit.lib.Ref;
64 import org.eclipse.jgit.lib.Repository;
65 import org.eclipse.osgi.util.NLS;
66 import org.eclipse.swt.SWT;
67 import org.eclipse.swt.accessibility.AccessibleAdapter;
68 import org.eclipse.swt.accessibility.AccessibleEvent;
69 import org.eclipse.swt.events.DisposeEvent;
70 import org.eclipse.swt.events.DisposeListener;
71 import org.eclipse.swt.events.KeyEvent;
72 import org.eclipse.swt.events.SelectionAdapter;
73 import org.eclipse.swt.events.SelectionEvent;
74 import org.eclipse.swt.graphics.Font;
75 import org.eclipse.swt.graphics.FontMetrics;
76 import org.eclipse.swt.graphics.GC;
77 import org.eclipse.swt.graphics.Image;
78 import org.eclipse.swt.graphics.Point;
79 import org.eclipse.swt.graphics.Resource;
80 import org.eclipse.swt.layout.GridData;
81 import org.eclipse.swt.widgets.Button;
82 import org.eclipse.swt.widgets.Composite;
83 import org.eclipse.swt.widgets.Control;
84 import org.eclipse.swt.widgets.Event;
85 import org.eclipse.swt.widgets.Label;
86 import org.eclipse.swt.widgets.Text;
87 import org.eclipse.swt.widgets.ToolBar;
88 import org.eclipse.swt.widgets.ToolItem;
89 import org.eclipse.swt.widgets.Widget;
90 import org.eclipse.ui.IEditorDescriptor;
91 import org.eclipse.ui.IEditorRegistry;
92 import org.eclipse.ui.ISelectionListener;
93 import org.eclipse.ui.ISharedImages;
94 import org.eclipse.ui.ISources;
95 import org.eclipse.ui.IWorkbench;
96 import org.eclipse.ui.IWorkbenchCommandConstants;
97 import org.eclipse.ui.IWorkbenchPart;
98 import org.eclipse.ui.IWorkbenchWindow;
99 import org.eclipse.ui.PlatformUI;
100 import org.eclipse.ui.actions.ContributionItemFactory;
101 import org.eclipse.ui.handlers.IHandlerService;
102 import org.eclipse.ui.keys.IBindingService;
103 import org.eclipse.ui.services.IServiceLocator;
106 * Some utilities for UI code
108 public class UIUtils {
110 private static final String CSS_CLASS_KEY = "org.eclipse.e4.ui.css.CssClassName"; //$NON-NLS-1$
112 private static final String CSS_DISABLED_KEY = "org.eclipse.e4.ui.css.disabled"; //$NON-NLS-1$
114 /** Default image descriptor for files */
115 public static final ImageDescriptor DEFAULT_FILE_IMG = PlatformUI
116 .getWorkbench().getSharedImages()
117 .getImageDescriptor(ISharedImages.IMG_OBJ_FILE);
120 * these activate the content assist; alphanumeric, space plus some expected
121 * special chars
123 private static final char[] VALUE_HELP_ACTIVATIONCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123457890*@ <>".toCharArray(); //$NON-NLS-1$
125 /** Vertical or horizontal spaces. */
126 private static final Pattern SPACES = Pattern.compile("(?:\\v|\\h)+"); //$NON-NLS-1$
129 * A keystroke for a "submit" action, see {@link #isSubmitKeyEvent(KeyEvent)}
131 public static final KeyStroke SUBMIT_KEY_STROKE = KeyStroke.getInstance(SWT.MOD1, SWT.CR);
134 * Handles a "previously used values" content assist.
135 * <p>
136 * Adding this to a text field will enable "content assist" by keeping track
137 * of the previously used valued for this field. The previously used values
138 * will be shown in the order they were last used (most recently used ones
139 * coming first in the list) and the number of entries is limited.
140 * <p>
141 * A "bulb" decorator will indicate that content assist is available for the
142 * field, and a tool tip is provided giving more information.
143 * <p>
144 * Content assist is activated by either typing in the field or by using a
145 * dedicated key stroke which is indicated in the tool tip. The list will be
146 * filtered with the content already in the text field with '*' being usable
147 * as wild card.
148 * <p>
149 * Note that the application must issue a call to {@link #updateProposals()}
150 * in order to add a new value to the "previously used values" list.
151 * <p>
152 * The list will be persisted in the plug-in dialog settings.
154 * @noextend not to be extended by clients
155 * @noimplement not to be implemented by clients, use
156 * {@link UIUtils#addPreviousValuesContentProposalToText(Text, String)}
157 * to create instances of this
159 public interface IPreviousValueProposalHandler {
161 * Updates the proposal list from the value in the text field.
162 * <p>
163 * The value will be truncated to the first 2000 characters in order to
164 * limit data size.
165 * <p>
166 * Note that this must be called in the UI thread, since it accesses the
167 * text field.
168 * <p>
169 * If the value is already in the list, it will become the first entry,
170 * otherwise it will be added at the beginning. Note that empty Strings
171 * will not be stored. The length of the list is limited, and the
172 * "oldest" entries will be removed once the limit is exceeded.
173 * <p>
174 * This call should only be issued if the value in the text field is
175 * "valid" in terms of the application.
177 public void updateProposals();
181 * A provider of candidate elements for which content proposals may be
182 * generated.
184 * @param <T>
185 * type of the candidate elements
187 public interface IContentProposalCandidateProvider<T> {
190 * Retrieves the collection of candidates eligible for content proposal
191 * generation.
193 * @return collection of candidates
195 public Collection<? extends T> getCandidates();
199 * A factory for creating {@link IContentProposal}s for {@link Ref}s.
201 * @param <T>
202 * type of elements to create proposals for
204 public interface IContentProposalFactory<T> {
207 * Gets a new {@link IContentProposal} for the given element. May or may
208 * not consider the {@link Pattern} and creates a proposal only if it
209 * matches the element with implementation-defined semantics.
211 * @param pattern
212 * constructed from current input to aid in selecting
213 * meaningful proposals; may be {@code null}
214 * @param element
215 * to consider creating a proposal for
216 * @return a new {@link IContentProposal}, or {@code null} if none
218 public IContentProposal getProposal(Pattern pattern, T element);
222 * A {@link ContentProposalAdapter} with a <em>public</em>
223 * {@link #openProposalPopup()} method.
225 public static class ExplicitContentProposalAdapter
226 extends ContentProposalAdapter {
229 * Construct a content proposal adapter that can assist the user with
230 * choosing content for the field.
232 * @param control
233 * the control for which the adapter is providing content
234 * assist. May not be {@code null}.
235 * @param controlContentAdapter
236 * the {@link IControlContentAdapter} used to obtain and
237 * update the control's contents as proposals are accepted.
238 * May not be {@code null}.
239 * @param proposalProvider
240 * the {@link IContentProposalProvider}> used to obtain
241 * content proposals for this control.
242 * @param keyStroke
243 * the keystroke that will invoke the content proposal popup.
244 * If this value is {@code null}, then proposals will be
245 * activated automatically when any of the auto activation
246 * characters are typed.
247 * @param autoActivationCharacters
248 * characters that trigger auto-activation of content
249 * proposal. If specified, these characters will trigger
250 * auto-activation of the proposal popup, regardless of
251 * whether an explicit invocation keyStroke was specified. If
252 * this parameter is {@code null}, then only a specified
253 * keyStroke will invoke content proposal. If this parameter
254 * is {@code null} and the keyStroke parameter is
255 * {@code null}, then all alphanumeric characters will
256 * auto-activate content proposal.
258 public ExplicitContentProposalAdapter(Control control,
259 IControlContentAdapter controlContentAdapter,
260 IContentProposalProvider proposalProvider,
261 KeyStroke keyStroke, char[] autoActivationCharacters) {
262 super(control, controlContentAdapter, proposalProvider, keyStroke,
263 autoActivationCharacters);
266 @Override
267 public void openProposalPopup() {
268 // Make this method accessible
269 super.openProposalPopup();
274 * @param id
275 * see {@link FontRegistry#get(String)}
276 * @return the font
278 public static Font getFont(final String id) {
279 return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme()
280 .getFontRegistry().get(id);
284 * @param id
285 * see {@link FontRegistry#getBold(String)}
286 * @return the font
288 public static Font getBoldFont(final String id) {
289 return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme()
290 .getFontRegistry().getBold(id);
294 * @param id
295 * see {@link FontRegistry#getItalic(String)}
296 * @return the font
298 public static Font getItalicFont(final String id) {
299 return PlatformUI.getWorkbench().getThemeManager().getCurrentTheme()
300 .getFontRegistry().getItalic(id);
304 * @return the indent of controls that depend on the previous control (e.g.
305 * a checkbox that is only enabled when the checkbox above it is
306 * checked)
308 public static int getControlIndent() {
309 // Eclipse 4.3: Use LayoutConstants.getIndent once we depend on 4.3
310 return 20;
314 * @param parent
315 * @param style
316 * @return a text field which is read-only but can be selected
318 public static Text createSelectableLabel(Composite parent, int style) {
319 // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=71765
320 Text text = new Text(parent, style | SWT.READ_ONLY);
321 text.setBackground(text.getDisplay().getSystemColor(
322 SWT.COLOR_WIDGET_BACKGROUND));
323 return text;
327 * Adds little bulb decoration to given control. Bulb will appear in top
328 * left corner of control after giving focus for this control.
330 * After clicking on bulb image text from <code>tooltip</code> will appear.
332 * @param control
333 * instance of {@link Control} object with should be decorated
334 * @param tooltip
335 * text value which should appear after clicking on bulb image.
336 * @return the {@link ControlDecoration} created
338 public static ControlDecoration addBulbDecorator(final Control control,
339 final String tooltip) {
340 ControlDecoration dec = new ControlDecoration(control, SWT.TOP
341 | SWT.LEFT);
343 dec.setImage(FieldDecorationRegistry.getDefault().getFieldDecoration(
344 FieldDecorationRegistry.DEC_CONTENT_PROPOSAL).getImage());
346 dec.setShowOnlyOnFocus(true);
347 dec.setShowHover(true);
349 dec.setDescriptionText(tooltip);
350 return dec;
354 * Creates a simple {@link Pattern} that can be used for matching content
355 * assist proposals. The pattern ignores leading blanks and allows '*' as a
356 * wildcard matching multiple arbitrary characters.
358 * @param content
359 * to create the pattern from
360 * @return the pattern, or {@code null} if none could be created
362 public static Pattern createProposalPattern(String content) {
363 // Make the simplest possible pattern check: allow "*"
364 // for multiple characters.
365 String patternString = content;
366 // Ignore spaces in the beginning.
367 while (patternString.length() > 0 && patternString.charAt(0) == ' ') {
368 patternString = patternString.substring(1);
371 // We quote the string as it may contain spaces
372 // and other stuff colliding with the pattern.
373 patternString = Pattern.quote(patternString);
375 patternString = patternString.replaceAll("\\x2A", ".*"); //$NON-NLS-1$ //$NON-NLS-2$
377 // Make sure we add a (logical) * at the end.
378 if (!patternString.endsWith(".*")) { //$NON-NLS-1$
379 patternString = patternString + ".*"; //$NON-NLS-1$
382 // Compile a case-insensitive pattern (assumes ASCII only).
383 Pattern pattern;
384 try {
385 pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
386 } catch (PatternSyntaxException e) {
387 pattern = null;
389 return pattern;
393 * Adds a "previously used values" content proposal handler to a text field.
394 * <p>
395 * The list will be limited to 10 values.
397 * @param textField
398 * the text field
399 * @param preferenceKey
400 * the key under which to store the "previously used values" in
401 * the dialog settings
402 * @return the handler the proposal handler
404 public static IPreviousValueProposalHandler addPreviousValuesContentProposalToText(
405 final Text textField, final String preferenceKey) {
406 KeyStroke stroke = UIUtils
407 .getKeystrokeOfBestActiveBindingFor(IWorkbenchCommandConstants.EDIT_CONTENT_ASSIST);
408 if (stroke == null)
409 addBulbDecorator(textField,
410 UIText.UIUtils_StartTypingForPreviousValuesMessage);
411 else
412 addBulbDecorator(
413 textField,
414 NLS.bind(UIText.UIUtils_PressShortcutMessage,
415 stroke.format()));
417 IContentProposalProvider cp = new IContentProposalProvider() {
419 @Override
420 public IContentProposal[] getProposals(String contents, int position) {
421 List<IContentProposal> resultList = new ArrayList<>();
423 Pattern pattern = createProposalPattern(contents);
424 String[] proposals = org.eclipse.egit.ui.Activator.getDefault()
425 .getDialogSettings().getArray(preferenceKey);
426 if (proposals != null) {
427 for (final String uriString : proposals) {
429 if (pattern != null
430 && !pattern.matcher(uriString).matches()) {
431 continue;
433 IContentProposal propsal = new IContentProposal() {
435 @Override
436 public String getLabel() {
437 return null;
440 @Override
441 public String getDescription() {
442 return null;
445 @Override
446 public int getCursorPosition() {
447 return 0;
450 @Override
451 public String getContent() {
452 return uriString;
455 resultList.add(propsal);
458 return resultList.toArray(new IContentProposal[0]);
462 ContentProposalAdapter adapter = new ContentProposalAdapter(textField,
463 new TextContentAdapter(), cp, stroke,
464 VALUE_HELP_ACTIVATIONCHARS);
465 // set the acceptance style to always replace the complete content
466 adapter.setProposalAcceptanceStyle(
467 ContentProposalAdapter.PROPOSAL_REPLACE);
469 return () -> {
470 String value = textField.getText();
471 // don't store empty values
472 if (value.length() > 0) {
473 // we don't want to save too much in the preferences
474 if (value.length() > 2000) {
475 value = value.substring(0, 1999);
477 // now we need to mix the value into the list
478 IDialogSettings settings = org.eclipse.egit.ui.Activator
479 .getDefault().getDialogSettings();
480 String[] existingValues = settings.getArray(preferenceKey);
481 if (existingValues == null) {
482 existingValues = new String[] { value };
483 settings.put(preferenceKey, existingValues);
484 } else {
486 List<String> values = new ArrayList<>(
487 existingValues.length + 1);
489 values.addAll(Arrays.asList(existingValues));
490 // if it is already the first value, we don't need to do
491 // anything
492 if (values.indexOf(value) == 0)
493 return;
495 values.remove(value);
496 // we insert at the top
497 values.add(0, value);
498 // make sure to not store more than the maximum number
499 // of values
500 while (values.size() > 10)
501 values.remove(values.size() - 1);
503 settings.put(preferenceKey, values.toArray(new String[0]));
510 * Adds a content proposal for {@link Ref}s (branches, tags...) to a text
511 * field
513 * @param textField
514 * the text field
515 * @param repository
516 * the repository
517 * @param refListProvider
518 * provides the {@link Ref}s to show in the proposal
519 * @param upstream
520 * {@code true} if the candidates provided by the
521 * {@code refListProvider} are from an upstream repository
522 * @return the content proposal adapter set on the {@code textField}
524 public static final ExplicitContentProposalAdapter addRefContentProposalToText(
525 Text textField,
526 Repository repository,
527 IContentProposalCandidateProvider<Ref> refListProvider,
528 boolean upstream) {
529 return UIUtils.<Ref> addContentProposalToText(textField,
530 refListProvider, (pattern, ref) -> {
531 String shortenedName = Repository
532 .shortenRefName(ref.getName());
533 if (pattern != null
534 && !pattern.matcher(ref.getName()).matches()
535 && !pattern.matcher(shortenedName).matches()) {
536 return null;
538 return new RefContentProposal(repository, ref, upstream);
539 }, null,
540 UIText.UIUtils_StartTypingForRemoteRefMessage,
541 UIText.UIUtils_PressShortcutForRemoteRefMessage);
545 * Adds a content proposal for arbitrary elements to a text field.
547 * @param <T>
548 * type of the proposal candidate objects
550 * @param textField
551 * the text field
552 * @param candidateProvider
553 * {@link IContentProposalCandidateProvider} providing the
554 * candidates eligible for creating {@link IContentProposal}s
555 * @param factory
556 * {@link IContentProposalFactory} to use to create proposals
557 * from candidates
558 * @param patternProvider
559 * to convert the current text of the field into a pattern
560 * suitable for filtering the candidates. If {@code null}, a
561 * default pattern is constructed using
562 * {@link #createProposalPattern(String)}.
563 * @param startTypingMessage
564 * hover message if no content assist key binding is active
565 * @param shortcutMessage
566 * hover message if a content assist key binding is active,
567 * should have a "{0}" placeholder that will be filled by the
568 * appropriate keystroke
569 * @return the content proposal adapter set on the {@code textField}
571 @NonNull
572 public static final <T> ExplicitContentProposalAdapter addContentProposalToText(
573 Text textField,
574 IContentProposalCandidateProvider<T> candidateProvider,
575 IContentProposalFactory<T> factory,
576 Function<String, Pattern> patternProvider,
577 String startTypingMessage,
578 String shortcutMessage) {
579 KeyStroke stroke = UIUtils
580 .getKeystrokeOfBestActiveBindingFor(IWorkbenchCommandConstants.EDIT_CONTENT_ASSIST);
581 if (stroke == null) {
582 addBulbDecorator(textField, startTypingMessage);
583 } else {
584 addBulbDecorator(textField,
585 NLS.bind(shortcutMessage, stroke.format()));
587 IContentProposalProvider cp = new IContentProposalProvider() {
588 @Override
589 public IContentProposal[] getProposals(String contents, int position) {
590 List<IContentProposal> resultList = new ArrayList<>();
592 Collection<? extends T> candidates = candidateProvider
593 .getCandidates();
594 if (candidates == null) {
595 return null;
597 Pattern pattern = patternProvider != null
598 ? patternProvider.apply(contents)
599 : createProposalPattern(contents);
600 for (final T candidate : candidates) {
601 IContentProposal proposal = factory.getProposal(pattern,
602 candidate);
603 if (proposal != null) {
604 resultList.add(proposal);
607 return resultList.toArray(new IContentProposal[0]);
611 ExplicitContentProposalAdapter adapter = new ExplicitContentProposalAdapter(
612 textField, new TextContentAdapter(), cp, stroke,
613 UIUtils.VALUE_HELP_ACTIVATIONCHARS);
614 // set the acceptance style to always replace the complete content
615 adapter.setProposalAcceptanceStyle(
616 ContentProposalAdapter.PROPOSAL_REPLACE);
617 return adapter;
621 * Set enabled state of the control and all its children
622 * @param control
623 * @param enable
625 public static void setEnabledRecursively(final Control control,
626 final boolean enable) {
627 control.setEnabled(enable);
628 if (control instanceof Composite)
629 for (final Control child : ((Composite) control).getChildren())
630 setEnabledRecursively(child, enable);
634 * Dispose of the resource when the widget is disposed
636 * @param widget
637 * @param resource
639 public static void hookDisposal(Widget widget, final Resource resource) {
640 if (widget == null || resource == null)
641 return;
643 widget.addDisposeListener(new DisposeListener() {
645 @Override
646 public void widgetDisposed(DisposeEvent e) {
647 resource.dispose();
653 * Dispose of the resource manager when the widget is disposed
655 * @param widget
656 * @param resources
658 public static void hookDisposal(Widget widget,
659 final ResourceManager resources) {
660 if (widget == null || resources == null)
661 return;
663 widget.addDisposeListener(new DisposeListener() {
665 @Override
666 public void widgetDisposed(DisposeEvent e) {
667 resources.dispose();
672 /** Key is file extension, value is the reference to the image descriptor */
673 private static Map<String, SoftReference<ImageDescriptor>> extensionToDescriptor = new HashMap<>();
676 * Get editor image for path
678 * @param path
679 * @return image descriptor
681 public static ImageDescriptor getEditorImage(final String path) {
682 if (path == null || path.length() <= 0) {
683 return DEFAULT_FILE_IMG;
685 final String fileName = new Path(path).lastSegment();
686 if (fileName == null) {
687 return DEFAULT_FILE_IMG;
689 IEditorRegistry registry = PlatformUI.getWorkbench()
690 .getEditorRegistry();
691 IEditorDescriptor defaultEditor = registry.getDefaultEditor(fileName);
692 if (defaultEditor != null) {
693 return defaultEditor.getImageDescriptor();
695 // now we know there is no Eclipse editor for the file, and Eclipse will
696 // check Program.findProgram() and this will be slow, see bug 464891
697 int extensionIndex = fileName.lastIndexOf('.');
698 if (extensionIndex < 0) {
699 // Program.findProgram() uses extensions only
700 return DEFAULT_FILE_IMG;
702 String key = fileName.substring(extensionIndex);
703 SoftReference<ImageDescriptor> cached = extensionToDescriptor.get(key);
704 if (cached != null) {
705 ImageDescriptor descriptor = cached.get();
706 if (descriptor != null) {
707 return descriptor;
710 // In worst case this calls Program.findProgram() and blocks UI
711 ImageDescriptor descriptor = registry.getImageDescriptor(fileName);
712 extensionToDescriptor.put(key, new SoftReference<>(descriptor));
713 return descriptor;
717 * Add expand all and collapse all toolbar items to the given toolbar bound
718 * to the given tree viewer
720 * @param toolbar
721 * @param viewer
722 * @return given toolbar
724 public static ToolBar addExpansionItems(final ToolBar toolbar,
725 final AbstractTreeViewer viewer) {
726 ToolItem collapseItem = new ToolItem(toolbar, SWT.PUSH);
727 Image collapseImage = UIIcons.COLLAPSEALL.createImage();
728 UIUtils.hookDisposal(collapseItem, collapseImage);
729 collapseItem.setImage(collapseImage);
730 collapseItem.setToolTipText(UIText.UIUtils_CollapseAll);
731 collapseItem.addSelectionListener(new SelectionAdapter() {
733 @Override
734 public void widgetSelected(SelectionEvent e) {
735 UIUtils.collapseAll(viewer);
740 ToolItem expandItem = new ToolItem(toolbar, SWT.PUSH);
741 Image expandImage = UIIcons.EXPAND_ALL.createImage();
742 UIUtils.hookDisposal(expandItem, expandImage);
743 expandItem.setImage(expandImage);
744 expandItem.setToolTipText(UIText.UIUtils_ExpandAll);
745 expandItem.addSelectionListener(new SelectionAdapter() {
747 @Override
748 public void widgetSelected(SelectionEvent e) {
749 UIUtils.expandAll(viewer);
753 return toolbar;
757 * Get dialog bound settings for given class using standard section name
759 * @param clazz
760 * @return dialog setting
762 public static IDialogSettings getDialogBoundSettings(final Class<?> clazz) {
763 return getDialogSettings(clazz.getName() + ".dialogBounds"); //$NON-NLS-1$
767 * Get dialog settings for given section name
769 * @param sectionName
770 * @return dialog settings
772 public static IDialogSettings getDialogSettings(final String sectionName) {
773 IDialogSettings settings = Activator.getDefault().getDialogSettings();
774 IDialogSettings section = settings.getSection(sectionName);
775 if (section == null)
776 section = settings.addNewSection(sectionName);
777 return section;
781 * Is viewer in a usable state?
783 * @param viewer
784 * @return true if usable, false if null or underlying control is null or
785 * disposed
787 public static boolean isUsable(final Viewer viewer) {
788 return viewer != null && isUsable(viewer.getControl());
792 * Is control usable?
794 * @param control
795 * @return true if usable, false if null or disposed
797 public static boolean isUsable(final Control control) {
798 return control != null && !control.isDisposed();
802 * Associate a label with a control to make it known to screen readers and
803 * similar accessibility tools.
805 * @param control
806 * to associate the label with
807 * @param label
808 * to associate with the control
810 public static void associateLabel(Control control, Label label) {
811 control.getAccessible().addAccessibleListener(new AccessibleAdapter() {
813 @Override
814 public void getName(AccessibleEvent e) {
815 e.result = label.getText();
821 * Run command with specified id
823 * @param service
824 * @param id
826 public static void executeCommand(IHandlerService service, String id) {
827 executeCommand(service, id, null);
831 * Run command with specified id
833 * @param service
834 * @param id
835 * @param event
837 public static void executeCommand(IHandlerService service, String id,
838 Event event) {
839 try {
840 service.executeCommand(id, event);
841 } catch (ExecutionException | NotDefinedException | NotEnabledException
842 | NotHandledException e) {
843 Activator.handleError(e.getMessage(), e, false);
848 * Determine if the key event represents a "submit" action
849 * (&lt;modifier&gt;+Enter).
851 * @param event
852 * @return true, if it means submit, false otherwise
854 public static boolean isSubmitKeyEvent(KeyEvent event) {
855 return (event.stateMask & SWT.MODIFIER_MASK) != 0
856 && event.keyCode == SUBMIT_KEY_STROKE.getNaturalKey();
860 * Prompt for saving all dirty editors for resources in the working
861 * directory of the specified repository.
863 * @param repository
864 * @return true, if the user opted to continue, false otherwise
865 * @see IWorkbench#saveAllEditors(boolean)
867 public static boolean saveAllEditors(Repository repository) {
868 return saveAllEditors(repository, null);
872 * Prompt for saving all dirty editors for resources in the working
873 * directory of the specified repository.
875 * If at least one file was saved, a dialog is displayed, asking the user if
876 * she wants to cancel the operation. Cancelling allows the user to do
877 * something with the newly saved files, before possibly restarting the
878 * operation.
880 * @param repository
881 * @param cancelConfirmationQuestion
882 * A string asking the user if she wants to cancel the operation.
883 * May be null to not open a dialog, but rather always continue.
884 * @return true, if the user opted to continue, false otherwise
885 * @see IWorkbench#saveAllEditors(boolean)
887 public static boolean saveAllEditors(Repository repository,
888 String cancelConfirmationQuestion) {
889 IWorkbench workbench = PlatformUI.getWorkbench();
890 IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
891 RepositorySaveableFilter filter = new RepositorySaveableFilter(
892 repository);
893 boolean success = workbench.saveAll(window, window, filter, true);
894 if (success && cancelConfirmationQuestion != null && filter.isAnythingSaved()){
895 // allow the user to cancel the operation to first do something with
896 // the newly saved files
897 String[] buttons = new String[] { IDialogConstants.YES_LABEL,
898 IDialogConstants.NO_LABEL };
899 MessageDialog dialog = new MessageDialog(window.getShell(),
900 UIText.CancelAfterSaveDialog_Title, null,
901 cancelConfirmationQuestion,
902 MessageDialog.QUESTION, buttons, 0) {
903 @Override
904 protected int getShellStyle() {
905 return (SWT.TITLE | SWT.BORDER | SWT.APPLICATION_MODAL
906 | SWT.SHEET | getDefaultOrientation());
909 int choice = dialog.open();
910 if (choice != 1) // user clicked "yes" or closed dialog -> cancel
911 return false;
913 return success;
917 * @param workbenchWindow the workbench window to use for creating the show in menu.
918 * @return the show in menu
920 public static MenuManager createShowInMenu(IWorkbenchWindow workbenchWindow) {
921 MenuManager showInSubMenu = new MenuManager(getShowInMenuLabel());
922 showInSubMenu.add(ContributionItemFactory.VIEWS_SHOW_IN.create(workbenchWindow));
923 return showInSubMenu;
926 private static String getShowInMenuLabel() {
927 IBindingService bindingService = Adapters.adapt(PlatformUI
928 .getWorkbench(), IBindingService.class);
929 if (bindingService != null) {
930 String keyBinding = bindingService
931 .getBestActiveBindingFormattedFor(IWorkbenchCommandConstants.NAVIGATE_SHOW_IN_QUICK_MENU);
932 if (keyBinding != null)
933 return UIText.UIUtils_ShowInMenuLabel + '\t' + keyBinding;
936 return UIText.UIUtils_ShowInMenuLabel;
940 * Look up best active binding's keystroke for the given command
942 * @param commandId
943 * The identifier of the command for which the best active
944 * binding's keystroke should be retrieved; must not be null.
945 * @return {@code KeyStroke} for the best active binding for the specified
946 * commandId or {@code null} if no binding is defined or if the
947 * binding service returns a {@code TriggerSequence} containing more
948 * than one {@code Trigger}.
950 @Nullable
951 public static KeyStroke getKeystrokeOfBestActiveBindingFor(String commandId) {
952 IBindingService bindingService = Adapters
953 .adapt(PlatformUI.getWorkbench(), IBindingService.class);
954 if (bindingService == null) {
955 return null;
957 TriggerSequence ts = bindingService.getBestActiveBindingFor(commandId);
958 if (ts == null)
959 return null;
961 Trigger[] triggers = ts.getTriggers();
962 if (triggers.length == 1 && triggers[0] instanceof KeyStroke)
963 return (KeyStroke) triggers[0];
964 else
965 return null;
969 * Copy from {@link org.eclipse.jface.dialogs.DialogPage} with changes to
970 * accommodate the lack of a Dialog context.
972 * @param button
973 * the button to set the <code>GridData</code>
975 public static void setButtonLayoutData(Button button) {
976 GC gc = new GC(button);
977 gc.setFont(JFaceResources.getDialogFont());
978 FontMetrics fontMetrics = gc.getFontMetrics();
979 gc.dispose();
981 GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
982 int widthHint = Dialog.convertHorizontalDLUsToPixels(fontMetrics,
983 IDialogConstants.BUTTON_WIDTH);
984 Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
985 data.widthHint = Math.max(widthHint, minSize.x);
986 button.setLayoutData(data);
990 * Locates the current part and selection and fires
991 * {@link ISelectionListener#selectionChanged(IWorkbenchPart, ISelection)}
992 * on the passed listener.
994 * @param serviceLocator
995 * @param selectionListener
997 public static void notifySelectionChangedWithCurrentSelection(
998 ISelectionListener selectionListener, IServiceLocator serviceLocator) {
999 IHandlerService handlerService = serviceLocator
1000 .getService(IHandlerService.class);
1001 IEvaluationContext state = handlerService.getCurrentState();
1002 // This seems to be the most reliable way to get the active part, it
1003 // also returns a part when it is called while creating a view that is
1004 // being shown.Getting the active part through the active workbench
1005 // window returned null in that case.
1006 Object partObject = state.getVariable(ISources.ACTIVE_PART_NAME);
1007 if (!(partObject instanceof IWorkbenchPart)) {
1008 partObject = state.getVariable(ISources.ACTIVE_EDITOR_NAME);
1010 Object selectionObject = state
1011 .getVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME);
1012 if (partObject instanceof IWorkbenchPart) {
1013 IWorkbenchPart part = (IWorkbenchPart) partObject;
1014 ISelection selection = selectionObject instanceof ISelection
1015 ? (ISelection) selectionObject
1016 : StructuredSelection.EMPTY;
1017 selectionListener.selectionChanged(part, selection);
1022 * Expand all tree nodes while disabling redraw.
1024 * @param viewer
1025 * tree viewer
1027 public static void expandAll(AbstractTreeViewer viewer) {
1028 // TODO: When 4.8 becomes the minimum target (including jface 3.14),
1029 // then this method can be replaced by
1030 // AbstractTreeViewer.expandAll(boolean)
1031 viewer.getControl().setRedraw(false);
1032 try {
1033 viewer.expandAll();
1034 } finally {
1035 viewer.getControl().setRedraw(true);
1040 * Collapse all tree nodes while disabling redraw.
1042 * @param viewer
1043 * tree viewer
1045 public static void collapseAll(AbstractTreeViewer viewer) {
1046 viewer.getControl().setRedraw(false);
1047 try {
1048 viewer.collapseAll();
1049 } finally {
1050 viewer.getControl().setRedraw(true);
1055 * Truncates a text to at most {@code maxLength} characters and replaces any
1056 * white space by single blanks.
1058 * @param text
1059 * to process
1060 * @param maxLength
1061 * of the result
1062 * @return the resulting shortened string, may be shorter than
1063 * {@code maxLength} characters
1065 public static String menuText(String text, int maxLength) {
1066 String result = Utils.shortenText(text, maxLength);
1067 return SPACES.matcher(result).replaceAll(" "); //$NON-NLS-1$
1071 * Sets whether CSS styling of a widget is enabled (the default) or not.
1073 * @param widget
1074 * to set the CSS styling mode on
1075 * @param enabled
1076 * {@code true} to enable CSS styling; {@code false} to disable
1077 * it
1079 public static void setCssStyling(Widget widget, boolean enabled) {
1080 widget.setData(CSS_DISABLED_KEY, Boolean.valueOf(!enabled));
1084 * Sets the CSS class name for a widget.
1086 * @param widget
1087 * to set the CSS class name on
1088 * @param cssClass
1089 * the CSS class value
1091 public static void setCssClass(Widget widget, String cssClass) {
1092 widget.setData(CSS_CLASS_KEY, cssClass);