1 /*******************************************************************************
2 * Copyright (c) 2010, 2015 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 v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * Mathias Kinzler (SAP AG) - initial implementation
10 *******************************************************************************/
11 package org
.eclipse
.egit
.ui
;
13 import java
.lang
.ref
.SoftReference
;
14 import java
.util
.ArrayList
;
15 import java
.util
.Collection
;
16 import java
.util
.HashMap
;
17 import java
.util
.HashSet
;
18 import java
.util
.LinkedHashSet
;
19 import java
.util
.List
;
21 import java
.util
.regex
.Pattern
;
22 import java
.util
.regex
.PatternSyntaxException
;
24 import org
.eclipse
.core
.commands
.ExecutionException
;
25 import org
.eclipse
.core
.commands
.NotEnabledException
;
26 import org
.eclipse
.core
.commands
.NotHandledException
;
27 import org
.eclipse
.core
.commands
.common
.NotDefinedException
;
28 import org
.eclipse
.core
.expressions
.IEvaluationContext
;
29 import org
.eclipse
.core
.runtime
.Path
;
30 import org
.eclipse
.egit
.core
.AdapterUtils
;
31 import org
.eclipse
.egit
.ui
.internal
.CommonUtils
;
32 import org
.eclipse
.egit
.ui
.internal
.RepositorySaveableFilter
;
33 import org
.eclipse
.egit
.ui
.internal
.UIIcons
;
34 import org
.eclipse
.egit
.ui
.internal
.UIText
;
35 import org
.eclipse
.egit
.ui
.internal
.components
.RefContentProposal
;
36 import org
.eclipse
.jgit
.annotations
.Nullable
;
37 import org
.eclipse
.jface
.action
.MenuManager
;
38 import org
.eclipse
.jface
.bindings
.Trigger
;
39 import org
.eclipse
.jface
.bindings
.TriggerSequence
;
40 import org
.eclipse
.jface
.bindings
.keys
.KeyStroke
;
41 import org
.eclipse
.jface
.dialogs
.Dialog
;
42 import org
.eclipse
.jface
.dialogs
.IDialogConstants
;
43 import org
.eclipse
.jface
.dialogs
.IDialogSettings
;
44 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
45 import org
.eclipse
.jface
.fieldassist
.ContentProposalAdapter
;
46 import org
.eclipse
.jface
.fieldassist
.ControlDecoration
;
47 import org
.eclipse
.jface
.fieldassist
.FieldDecorationRegistry
;
48 import org
.eclipse
.jface
.fieldassist
.IContentProposal
;
49 import org
.eclipse
.jface
.fieldassist
.IContentProposalProvider
;
50 import org
.eclipse
.jface
.fieldassist
.TextContentAdapter
;
51 import org
.eclipse
.jface
.resource
.FontRegistry
;
52 import org
.eclipse
.jface
.resource
.ImageDescriptor
;
53 import org
.eclipse
.jface
.resource
.JFaceResources
;
54 import org
.eclipse
.jface
.resource
.ResourceManager
;
55 import org
.eclipse
.jface
.text
.BadLocationException
;
56 import org
.eclipse
.jface
.text
.IDocument
;
57 import org
.eclipse
.jface
.text
.IRegion
;
58 import org
.eclipse
.jface
.text
.ITextViewer
;
59 import org
.eclipse
.jface
.text
.hyperlink
.IHyperlink
;
60 import org
.eclipse
.jface
.text
.hyperlink
.IHyperlinkDetector
;
61 import org
.eclipse
.jface
.viewers
.AbstractTreeViewer
;
62 import org
.eclipse
.jface
.viewers
.ISelection
;
63 import org
.eclipse
.jface
.viewers
.Viewer
;
64 import org
.eclipse
.jgit
.lib
.Ref
;
65 import org
.eclipse
.jgit
.lib
.Repository
;
66 import org
.eclipse
.osgi
.util
.NLS
;
67 import org
.eclipse
.swt
.SWT
;
68 import org
.eclipse
.swt
.custom
.StyleRange
;
69 import org
.eclipse
.swt
.custom
.StyledText
;
70 import org
.eclipse
.swt
.events
.DisposeEvent
;
71 import org
.eclipse
.swt
.events
.DisposeListener
;
72 import org
.eclipse
.swt
.events
.KeyEvent
;
73 import org
.eclipse
.swt
.events
.SelectionAdapter
;
74 import org
.eclipse
.swt
.events
.SelectionEvent
;
75 import org
.eclipse
.swt
.graphics
.Font
;
76 import org
.eclipse
.swt
.graphics
.FontMetrics
;
77 import org
.eclipse
.swt
.graphics
.GC
;
78 import org
.eclipse
.swt
.graphics
.Image
;
79 import org
.eclipse
.swt
.graphics
.ImageData
;
80 import org
.eclipse
.swt
.graphics
.Point
;
81 import org
.eclipse
.swt
.graphics
.Resource
;
82 import org
.eclipse
.swt
.layout
.GridData
;
83 import org
.eclipse
.swt
.widgets
.Button
;
84 import org
.eclipse
.swt
.widgets
.Composite
;
85 import org
.eclipse
.swt
.widgets
.Control
;
86 import org
.eclipse
.swt
.widgets
.Display
;
87 import org
.eclipse
.swt
.widgets
.Event
;
88 import org
.eclipse
.swt
.widgets
.Text
;
89 import org
.eclipse
.swt
.widgets
.ToolBar
;
90 import org
.eclipse
.swt
.widgets
.ToolItem
;
91 import org
.eclipse
.swt
.widgets
.Widget
;
92 import org
.eclipse
.ui
.IEditorDescriptor
;
93 import org
.eclipse
.ui
.IEditorRegistry
;
94 import org
.eclipse
.ui
.ISelectionListener
;
95 import org
.eclipse
.ui
.ISharedImages
;
96 import org
.eclipse
.ui
.ISources
;
97 import org
.eclipse
.ui
.IWorkbench
;
98 import org
.eclipse
.ui
.IWorkbenchCommandConstants
;
99 import org
.eclipse
.ui
.IWorkbenchPart
;
100 import org
.eclipse
.ui
.IWorkbenchWindow
;
101 import org
.eclipse
.ui
.PlatformUI
;
102 import org
.eclipse
.ui
.actions
.ContributionItemFactory
;
103 import org
.eclipse
.ui
.handlers
.IHandlerService
;
104 import org
.eclipse
.ui
.keys
.IBindingService
;
105 import org
.eclipse
.ui
.services
.IServiceLocator
;
108 * Some utilities for UI code
110 public class UIUtils
{
112 /** Default image descriptor for files */
113 public static final ImageDescriptor DEFAULT_FILE_IMG
= PlatformUI
114 .getWorkbench().getSharedImages()
115 .getImageDescriptor(ISharedImages
.IMG_OBJ_FILE
);
118 * these activate the content assist; alphanumeric, space plus some expected
121 private static final char[] VALUE_HELP_ACTIVATIONCHARS
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123457890*@ <>".toCharArray(); //$NON-NLS-1$
124 * A keystroke for a "submit" action, see {@link #isSubmitKeyEvent(KeyEvent)}
126 public static final KeyStroke SUBMIT_KEY_STROKE
= KeyStroke
.getInstance(SWT
.MOD1
, SWT
.CR
);
129 * Handles a "previously used values" content assist.
131 * Adding this to a text field will enable "content assist" by keeping track
132 * of the previously used valued for this field. The previously used values
133 * will be shown in the order they were last used (most recently used ones
134 * coming first in the list) and the number of entries is limited.
136 * A "bulb" decorator will indicate that content assist is available for the
137 * field, and a tool tip is provided giving more information.
139 * Content assist is activated by either typing in the field or by using a
140 * dedicated key stroke which is indicated in the tool tip. The list will be
141 * filtered with the content already in the text field with '*' being usable
144 * Note that the application must issue a call to {@link #updateProposals()}
145 * in order to add a new value to the "previously used values" list.
147 * The list will be persisted in the plug-in dialog settings.
149 * @noextend not to be extended by clients
150 * @noimplement not to be implemented by clients, use
151 * {@link UIUtils#addPreviousValuesContentProposalToText(Text, String)}
152 * to create instances of this
154 public interface IPreviousValueProposalHandler
{
156 * Updates the proposal list from the value in the text field.
158 * The value will be truncated to the first 2000 characters in order to
161 * Note that this must be called in the UI thread, since it accesses the
164 * If the value is already in the list, it will become the first entry,
165 * otherwise it will be added at the beginning. Note that empty Strings
166 * will not be stored. The length of the list is limited, and the
167 * "oldest" entries will be removed once the limit is exceeded.
169 * This call should only be issued if the value in the text field is
170 * "valid" in terms of the application.
172 public void updateProposals();
176 * A provider of candidate elements for which content proposals may be
180 * type of the candidate elements
182 public interface IContentProposalCandidateProvider
<T
> {
185 * Retrieves the collection of candidates eligible for content proposal
188 * @return collection of candidates
190 public Collection
<?
extends T
> getCandidates();
194 * A factory for creating {@link IContentProposal}s for {@link Ref}s.
197 * type of elements to create proposals for
199 public interface IContentProposalFactory
<T
> {
202 * Gets a new {@link IContentProposal} for the given element. May or may
203 * not consider the {@link Pattern} and creates a proposal only if it
204 * matches the element with implementation-defined semantics.
207 * constructed from current input to aid in selecting
208 * meaningful proposals; may be {@code null}
210 * to consider creating a proposal for
211 * @return a new {@link IContentProposal}, or {@code null} if none
213 public IContentProposal
getProposal(Pattern pattern
, T element
);
218 * see {@link FontRegistry#get(String)}
221 public static Font
getFont(final String id
) {
222 return PlatformUI
.getWorkbench().getThemeManager().getCurrentTheme()
223 .getFontRegistry().get(id
);
228 * see {@link FontRegistry#getBold(String)}
231 public static Font
getBoldFont(final String id
) {
232 return PlatformUI
.getWorkbench().getThemeManager().getCurrentTheme()
233 .getFontRegistry().getBold(id
);
238 * see {@link FontRegistry#getItalic(String)}
241 public static Font
getItalicFont(final String id
) {
242 return PlatformUI
.getWorkbench().getThemeManager().getCurrentTheme()
243 .getFontRegistry().getItalic(id
);
247 * @return the indent of controls that depend on the previous control (e.g.
248 * a checkbox that is only enabled when the checkbox above it is
251 public static int getControlIndent() {
252 // Eclipse 4.3: Use LayoutConstants.getIndent once we depend on 4.3
259 * @return a text field which is read-only but can be selected
261 public static Text
createSelectableLabel(Composite parent
, int style
) {
262 // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=71765
263 Text text
= new Text(parent
, style
| SWT
.READ_ONLY
);
264 text
.setBackground(text
.getDisplay().getSystemColor(
265 SWT
.COLOR_WIDGET_BACKGROUND
));
270 * Adds little bulb decoration to given control. Bulb will appear in top
271 * left corner of control after giving focus for this control.
273 * After clicking on bulb image text from <code>tooltip</code> will appear.
276 * instance of {@link Control} object with should be decorated
278 * text value which should appear after clicking on bulb image.
279 * @return the {@link ControlDecoration} created
281 public static ControlDecoration
addBulbDecorator(final Control control
,
282 final String tooltip
) {
283 ControlDecoration dec
= new ControlDecoration(control
, SWT
.TOP
286 dec
.setImage(FieldDecorationRegistry
.getDefault().getFieldDecoration(
287 FieldDecorationRegistry
.DEC_CONTENT_PROPOSAL
).getImage());
289 dec
.setShowOnlyOnFocus(true);
290 dec
.setShowHover(true);
292 dec
.setDescriptionText(tooltip
);
297 * Creates a simple {@link Pattern} that can be used for matching content
298 * assist proposals. The pattern ignores leading blanks and allows '*' as a
299 * wildcard matching multiple arbitrary characters.
302 * to create the pattern from
303 * @return the pattern, or {@code null} if none could be created
305 public static Pattern
createProposalPattern(String content
) {
306 // Make the simplest possible pattern check: allow "*"
307 // for multiple characters.
308 String patternString
= content
;
309 // Ignore spaces in the beginning.
310 while (patternString
.length() > 0 && patternString
.charAt(0) == ' ') {
311 patternString
= patternString
.substring(1);
314 // We quote the string as it may contain spaces
315 // and other stuff colliding with the pattern.
316 patternString
= Pattern
.quote(patternString
);
318 patternString
= patternString
.replaceAll("\\x2A", ".*"); //$NON-NLS-1$ //$NON-NLS-2$
320 // Make sure we add a (logical) * at the end.
321 if (!patternString
.endsWith(".*")) { //$NON-NLS-1$
322 patternString
= patternString
+ ".*"; //$NON-NLS-1$
325 // Compile a case-insensitive pattern (assumes ASCII only).
328 pattern
= Pattern
.compile(patternString
, Pattern
.CASE_INSENSITIVE
);
329 } catch (PatternSyntaxException e
) {
336 * Adds a "previously used values" content proposal handler to a text field.
338 * The list will be limited to 10 values.
342 * @param preferenceKey
343 * the key under which to store the "previously used values" in
344 * the dialog settings
345 * @return the handler the proposal handler
347 public static IPreviousValueProposalHandler
addPreviousValuesContentProposalToText(
348 final Text textField
, final String preferenceKey
) {
349 KeyStroke stroke
= UIUtils
350 .getKeystrokeOfBestActiveBindingFor(IWorkbenchCommandConstants
.EDIT_CONTENT_ASSIST
);
352 addBulbDecorator(textField
,
353 UIText
.UIUtils_StartTypingForPreviousValuesMessage
);
357 NLS
.bind(UIText
.UIUtils_PressShortcutMessage
,
360 IContentProposalProvider cp
= new IContentProposalProvider() {
363 public IContentProposal
[] getProposals(String contents
, int position
) {
364 List
<IContentProposal
> resultList
= new ArrayList
<>();
366 Pattern pattern
= createProposalPattern(contents
);
367 String
[] proposals
= org
.eclipse
.egit
.ui
.Activator
.getDefault()
368 .getDialogSettings().getArray(preferenceKey
);
369 if (proposals
!= null) {
370 for (final String uriString
: proposals
) {
373 && !pattern
.matcher(uriString
).matches()) {
376 IContentProposal propsal
= new IContentProposal() {
379 public String
getLabel() {
384 public String
getDescription() {
389 public int getCursorPosition() {
394 public String
getContent() {
398 resultList
.add(propsal
);
401 return resultList
.toArray(new IContentProposal
[resultList
406 ContentProposalAdapter adapter
= new ContentProposalAdapter(textField
,
407 new TextContentAdapter(), cp
, stroke
,
408 VALUE_HELP_ACTIVATIONCHARS
);
409 // set the acceptance style to always replace the complete content
410 adapter
.setProposalAcceptanceStyle(
411 ContentProposalAdapter
.PROPOSAL_REPLACE
);
413 return new IPreviousValueProposalHandler() {
415 public void updateProposals() {
416 String value
= textField
.getText();
417 // don't store empty values
418 if (value
.length() > 0) {
419 // we don't want to save too much in the preferences
420 if (value
.length() > 2000) {
421 value
= value
.substring(0, 1999);
423 // now we need to mix the value into the list
424 IDialogSettings settings
= org
.eclipse
.egit
.ui
.Activator
425 .getDefault().getDialogSettings();
426 String
[] existingValues
= settings
.getArray(preferenceKey
);
427 if (existingValues
== null) {
428 existingValues
= new String
[] { value
};
429 settings
.put(preferenceKey
, existingValues
);
432 List
<String
> values
= new ArrayList
<>(
433 existingValues
.length
+ 1);
435 for (String existingValue
: existingValues
)
436 values
.add(existingValue
);
437 // if it is already the first value, we don't need to do
439 if (values
.indexOf(value
) == 0)
442 values
.remove(value
);
443 // we insert at the top
444 values
.add(0, value
);
445 // make sure to not store more than the maximum number
447 while (values
.size() > 10)
448 values
.remove(values
.size() - 1);
450 settings
.put(preferenceKey
, values
451 .toArray(new String
[values
.size()]));
459 * Adds a content proposal for {@link Ref}s (branches, tags...) to a text
466 * @param refListProvider
467 * provides the {@link Ref}s to show in the proposal
469 public static final void addRefContentProposalToText(Text textField
,
470 Repository repository
,
471 IContentProposalCandidateProvider
<Ref
> refListProvider
) {
472 UIUtils
.<Ref
> addContentProposalToText(textField
,
473 refListProvider
, (pattern
, ref
) -> {
474 String shortenedName
= Repository
475 .shortenRefName(ref
.getName());
477 && !pattern
.matcher(ref
.getName()).matches()
478 && !pattern
.matcher(shortenedName
).matches()) {
481 return new RefContentProposal(repository
, ref
);
482 }, UIText
.UIUtils_StartTypingForRemoteRefMessage
,
483 UIText
.UIUtils_PressShortcutForRemoteRefMessage
);
487 * Adds a content proposal for arbitrary elements to a text field.
490 * type of the proposal candidate objects
494 * @param candidateProvider
495 * {@link IContentProposalCandidateProvider} providing the
496 * candidates eligible for creating {@link IContentProposal}s
498 * {@link IContentProposalFactory} to use to create proposals
500 * @param startTypingMessage
501 * hover message if no content assist key binding is active
502 * @param shortcutMessage
503 * hover message if a content assist key binding is active,
504 * should have a "{0}" placeholder that will be filled by the
505 * appropriate keystroke
507 public static final <T
> void addContentProposalToText(Text textField
,
508 IContentProposalCandidateProvider
<T
> candidateProvider
,
509 IContentProposalFactory
<T
> factory
, String startTypingMessage
,
510 String shortcutMessage
) {
511 KeyStroke stroke
= UIUtils
512 .getKeystrokeOfBestActiveBindingFor(IWorkbenchCommandConstants
.EDIT_CONTENT_ASSIST
);
513 if (stroke
== null) {
514 addBulbDecorator(textField
, startTypingMessage
);
516 addBulbDecorator(textField
,
517 NLS
.bind(shortcutMessage
, stroke
.format()));
519 IContentProposalProvider cp
= new IContentProposalProvider() {
521 public IContentProposal
[] getProposals(String contents
, int position
) {
522 List
<IContentProposal
> resultList
= new ArrayList
<>();
524 Pattern pattern
= createProposalPattern(contents
);
525 Collection
<?
extends T
> candidates
= candidateProvider
527 if (candidates
!= null) {
528 for (final T candidate
: candidates
) {
529 IContentProposal proposal
= factory
.getProposal(pattern
,
531 if (proposal
!= null) {
532 resultList
.add(proposal
);
536 return resultList
.toArray(new IContentProposal
[resultList
541 ContentProposalAdapter adapter
= new ContentProposalAdapter(textField
,
542 new TextContentAdapter(), cp
, stroke
,
543 UIUtils
.VALUE_HELP_ACTIVATIONCHARS
);
544 // set the acceptance style to always replace the complete content
545 adapter
.setProposalAcceptanceStyle(
546 ContentProposalAdapter
.PROPOSAL_REPLACE
);
550 * Set enabled state of the control and all its children
554 public static void setEnabledRecursively(final Control control
,
555 final boolean enable
) {
556 control
.setEnabled(enable
);
557 if (control
instanceof Composite
)
558 for (final Control child
: ((Composite
) control
).getChildren())
559 setEnabledRecursively(child
, enable
);
563 * Dispose of the resource when the widget is disposed
568 public static void hookDisposal(Widget widget
, final Resource resource
) {
569 if (widget
== null || resource
== null)
572 widget
.addDisposeListener(new DisposeListener() {
575 public void widgetDisposed(DisposeEvent e
) {
582 * Dispose of the resource manager when the widget is disposed
587 public static void hookDisposal(Widget widget
,
588 final ResourceManager resources
) {
589 if (widget
== null || resources
== null)
592 widget
.addDisposeListener(new DisposeListener() {
595 public void widgetDisposed(DisposeEvent e
) {
601 /** Key is file extension, value is the reference to the image descriptor */
602 private static Map
<String
, SoftReference
<ImageDescriptor
>> extensionToDescriptor
= new HashMap
<>();
605 * Get editor image for path
608 * @return image descriptor
610 public static ImageDescriptor
getEditorImage(final String path
) {
611 if (path
== null || path
.length() <= 0) {
612 return DEFAULT_FILE_IMG
;
614 final String fileName
= new Path(path
).lastSegment();
615 if (fileName
== null) {
616 return DEFAULT_FILE_IMG
;
618 IEditorRegistry registry
= PlatformUI
.getWorkbench()
619 .getEditorRegistry();
620 IEditorDescriptor defaultEditor
= registry
.getDefaultEditor(fileName
);
621 if (defaultEditor
!= null) {
622 return defaultEditor
.getImageDescriptor();
624 // now we know there is no Eclipse editor for the file, and Eclipse will
625 // check Program.findProgram() and this will be slow, see bug 464891
626 int extensionIndex
= fileName
.lastIndexOf('.');
627 if (extensionIndex
< 0) {
628 // Program.findProgram() uses extensions only
629 return DEFAULT_FILE_IMG
;
631 String key
= fileName
.substring(extensionIndex
);
632 SoftReference
<ImageDescriptor
> cached
= extensionToDescriptor
.get(key
);
633 if (cached
!= null) {
634 ImageDescriptor descriptor
= cached
.get();
635 if (descriptor
!= null) {
639 // In worst case this calls Program.findProgram() and blocks UI
640 ImageDescriptor descriptor
= registry
.getImageDescriptor(fileName
);
641 extensionToDescriptor
.put(key
, new SoftReference
<>(descriptor
));
646 * Get size of image descriptor as point.
651 public static Point
getSize(ImageDescriptor descriptor
) {
652 ImageData data
= descriptor
.getImageData();
654 return new Point(0, 0);
655 return new Point(data
.width
, data
.height
);
659 * Add expand all and collapse all toolbar items to the given toolbar bound
660 * to the given tree viewer
664 * @return given toolbar
666 public static ToolBar
addExpansionItems(final ToolBar toolbar
,
667 final AbstractTreeViewer viewer
) {
668 ToolItem collapseItem
= new ToolItem(toolbar
, SWT
.PUSH
);
669 Image collapseImage
= UIIcons
.COLLAPSEALL
.createImage();
670 UIUtils
.hookDisposal(collapseItem
, collapseImage
);
671 collapseItem
.setImage(collapseImage
);
672 collapseItem
.setToolTipText(UIText
.UIUtils_CollapseAll
);
673 collapseItem
.addSelectionListener(new SelectionAdapter() {
676 public void widgetSelected(SelectionEvent e
) {
677 viewer
.collapseAll();
682 ToolItem expandItem
= new ToolItem(toolbar
, SWT
.PUSH
);
683 Image expandImage
= UIIcons
.EXPAND_ALL
.createImage();
684 UIUtils
.hookDisposal(expandItem
, expandImage
);
685 expandItem
.setImage(expandImage
);
686 expandItem
.setToolTipText(UIText
.UIUtils_ExpandAll
);
687 expandItem
.addSelectionListener(new SelectionAdapter() {
690 public void widgetSelected(SelectionEvent e
) {
699 * Get dialog bound settings for given class using standard section name
702 * @return dialog setting
704 public static IDialogSettings
getDialogBoundSettings(final Class
<?
> clazz
) {
705 return getDialogSettings(clazz
.getName() + ".dialogBounds"); //$NON-NLS-1$
709 * Get dialog settings for given section name
712 * @return dialog settings
714 public static IDialogSettings
getDialogSettings(final String sectionName
) {
715 IDialogSettings settings
= Activator
.getDefault().getDialogSettings();
716 IDialogSettings section
= settings
.getSection(sectionName
);
718 section
= settings
.addNewSection(sectionName
);
723 * Is viewer in a usable state?
726 * @return true if usable, false if null or underlying control is null or
729 public static boolean isUsable(final Viewer viewer
) {
730 return viewer
!= null && isUsable(viewer
.getControl());
737 * @return true if usable, false if null or disposed
739 public static boolean isUsable(final Control control
) {
740 return control
!= null && !control
.isDisposed();
744 * Run command with specified id
749 public static void executeCommand(IHandlerService service
, String id
) {
750 executeCommand(service
, id
, null);
754 * Run command with specified id
760 public static void executeCommand(IHandlerService service
, String id
,
763 service
.executeCommand(id
, event
);
764 } catch (ExecutionException e
) {
765 Activator
.handleError(e
.getMessage(), e
, false);
766 } catch (NotDefinedException e
) {
767 Activator
.handleError(e
.getMessage(), e
, false);
768 } catch (NotEnabledException e
) {
769 Activator
.handleError(e
.getMessage(), e
, false);
770 } catch (NotHandledException e
) {
771 Activator
.handleError(e
.getMessage(), e
, false);
776 * Determine if the key event represents a "submit" action
777 * (<modifier>+Enter).
780 * @return true, if it means submit, false otherwise
782 public static boolean isSubmitKeyEvent(KeyEvent event
) {
783 return (event
.stateMask
& SWT
.MODIFIER_MASK
) != 0
784 && event
.keyCode
== SUBMIT_KEY_STROKE
.getNaturalKey();
788 * Prompt for saving all dirty editors for resources in the working
789 * directory of the specified repository.
792 * @return true, if the user opted to continue, false otherwise
793 * @see IWorkbench#saveAllEditors(boolean)
795 public static boolean saveAllEditors(Repository repository
) {
796 return saveAllEditors(repository
, null);
800 * Prompt for saving all dirty editors for resources in the working
801 * directory of the specified repository.
803 * If at least one file was saved, a dialog is displayed, asking the user if
804 * she wants to cancel the operation. Cancelling allows the user to do
805 * something with the newly saved files, before possibly restarting the
809 * @param cancelConfirmationQuestion
810 * A string asking the user if she wants to cancel the operation.
811 * May be null to not open a dialog, but rather always continue.
812 * @return true, if the user opted to continue, false otherwise
813 * @see IWorkbench#saveAllEditors(boolean)
815 public static boolean saveAllEditors(Repository repository
,
816 String cancelConfirmationQuestion
) {
817 IWorkbench workbench
= PlatformUI
.getWorkbench();
818 IWorkbenchWindow window
= workbench
.getActiveWorkbenchWindow();
819 RepositorySaveableFilter filter
= new RepositorySaveableFilter(
821 boolean success
= workbench
.saveAll(window
, window
, filter
, true);
822 if (success
&& cancelConfirmationQuestion
!= null && filter
.isAnythingSaved()){
823 // allow the user to cancel the operation to first do something with
824 // the newly saved files
825 String
[] buttons
= new String
[] { IDialogConstants
.YES_LABEL
,
826 IDialogConstants
.NO_LABEL
};
827 MessageDialog dialog
= new MessageDialog(window
.getShell(),
828 UIText
.CancelAfterSaveDialog_Title
, null,
829 cancelConfirmationQuestion
,
830 MessageDialog
.QUESTION
, buttons
, 0) {
832 protected int getShellStyle() {
833 return (SWT
.TITLE
| SWT
.BORDER
| SWT
.APPLICATION_MODAL
834 | SWT
.SHEET
| getDefaultOrientation());
837 int choice
= dialog
.open();
838 if (choice
!= 1) // user clicked "yes" or closed dialog -> cancel
845 * @param workbenchWindow the workbench window to use for creating the show in menu.
846 * @return the show in menu
848 public static MenuManager
createShowInMenu(IWorkbenchWindow workbenchWindow
) {
849 MenuManager showInSubMenu
= new MenuManager(getShowInMenuLabel());
850 showInSubMenu
.add(ContributionItemFactory
.VIEWS_SHOW_IN
.create(workbenchWindow
));
851 return showInSubMenu
;
855 * Use hyperlink detectors to find a text viewer's hyperlinks and apply them
856 * to the text widget. Existing overlapping styles are overwritten by new
860 * @param hyperlinkDetectors
861 * @deprecated Instead of applying SWT styling directly use JFace
863 * {@link org.eclipse.jface.text.rules.DefaultDamagerRepairer
864 * DefaultDamagerRepairer},
865 * {@link org.eclipse.jface.text.rules.ITokenScanner
866 * ITokenScanner}) to do syntax coloring. See also
867 * {@link org.eclipse.egit.ui.internal.dialogs.HyperlinkTokenScanner}
871 public static void applyHyperlinkDetectorStyleRanges(
872 ITextViewer textViewer
, IHyperlinkDetector
[] hyperlinkDetectors
) {
873 StyleRange
[] styleRanges
= getHyperlinkDetectorStyleRanges(textViewer
,
875 StyledText styledText
= textViewer
.getTextWidget();
876 // Apply hyperlink style ranges one by one. setStyleRange takes care to
877 // do the right thing in case they overlap with an existing style range.
878 for (StyleRange styleRange
: styleRanges
)
879 styledText
.setStyleRange(styleRange
);
883 * Use hyperlink detectors to find a text viewer's hyperlinks and return the
884 * style ranges to render them.
887 * @param hyperlinkDetectors
888 * @return the style ranges to render the detected hyperlinks
889 * @deprecated Instead of applying SWT styling directly use JFace
891 * {@link org.eclipse.jface.text.rules.DefaultDamagerRepairer
892 * DefaultDamagerRepairer},
893 * {@link org.eclipse.jface.text.rules.ITokenScanner
894 * ITokenScanner}) to do syntax coloring. See also
895 * {@link org.eclipse.egit.ui.internal.dialogs.HyperlinkTokenScanner}
899 public static StyleRange
[] getHyperlinkDetectorStyleRanges(
900 ITextViewer textViewer
, IHyperlinkDetector
[] hyperlinkDetectors
) {
901 HashSet
<StyleRange
> styleRangeList
= new LinkedHashSet
<>();
902 if (hyperlinkDetectors
!= null && hyperlinkDetectors
.length
> 0) {
903 IDocument doc
= textViewer
.getDocument();
904 for (int line
= 0; line
< doc
.getNumberOfLines(); line
++) {
906 IRegion region
= doc
.getLineInformation(line
);
907 for (IHyperlinkDetector hyperLinkDetector
: hyperlinkDetectors
) {
908 IHyperlink
[] hyperlinks
= hyperLinkDetector
909 .detectHyperlinks(textViewer
, region
, true);
910 if (hyperlinks
!= null) {
911 for (IHyperlink hyperlink
: hyperlinks
) {
912 StyleRange hyperlinkStyleRange
= new StyleRange(
913 hyperlink
.getHyperlinkRegion()
914 .getOffset(), hyperlink
915 .getHyperlinkRegion()
916 .getLength(), Display
917 .getDefault().getSystemColor(
919 Display
.getDefault().getSystemColor(
921 hyperlinkStyleRange
.underline
= true;
922 styleRangeList
.add(hyperlinkStyleRange
);
926 } catch (BadLocationException e
) {
927 Activator
.logError(e
.getMessage(), e
);
932 StyleRange
[] styleRangeArray
= new StyleRange
[styleRangeList
.size()];
933 styleRangeList
.toArray(styleRangeArray
);
934 return styleRangeArray
;
937 private static String
getShowInMenuLabel() {
938 IBindingService bindingService
= AdapterUtils
.adapt(PlatformUI
939 .getWorkbench(), IBindingService
.class);
940 if (bindingService
!= null) {
941 String keyBinding
= bindingService
942 .getBestActiveBindingFormattedFor(IWorkbenchCommandConstants
.NAVIGATE_SHOW_IN_QUICK_MENU
);
943 if (keyBinding
!= null)
944 return UIText
.UIUtils_ShowInMenuLabel
+ '\t' + keyBinding
;
947 return UIText
.UIUtils_ShowInMenuLabel
;
951 * Look up best active binding's keystroke for the given command
954 * The identifier of the command for which the best active
955 * binding's keystroke should be retrieved; must not be null.
956 * @return {@code KeyStroke} for the best active binding for the specified
957 * commandId or {@code null} if no binding is defined or if the
958 * binding service returns a {@code TriggerSequence} containing more
959 * than one {@code Trigger}.
962 public static KeyStroke
getKeystrokeOfBestActiveBindingFor(String commandId
) {
963 IBindingService bindingService
= AdapterUtils
964 .adapt(PlatformUI
.getWorkbench(), IBindingService
.class);
965 if (bindingService
== null) {
968 TriggerSequence ts
= bindingService
.getBestActiveBindingFor(commandId
);
972 Trigger
[] triggers
= ts
.getTriggers();
973 if (triggers
.length
== 1 && triggers
[0] instanceof KeyStroke
)
974 return (KeyStroke
) triggers
[0];
980 * Copy from {@link org.eclipse.jface.dialogs.DialogPage} with changes to
981 * accommodate the lack of a Dialog context.
984 * the button to set the <code>GridData</code>
986 public static void setButtonLayoutData(Button button
) {
987 GC gc
= new GC(button
);
988 gc
.setFont(JFaceResources
.getDialogFont());
989 FontMetrics fontMetrics
= gc
.getFontMetrics();
992 GridData data
= new GridData(GridData
.HORIZONTAL_ALIGN_FILL
);
993 int widthHint
= Dialog
.convertHorizontalDLUsToPixels(fontMetrics
,
994 IDialogConstants
.BUTTON_WIDTH
);
995 Point minSize
= button
.computeSize(SWT
.DEFAULT
, SWT
.DEFAULT
, true);
996 data
.widthHint
= Math
.max(widthHint
, minSize
.x
);
997 button
.setLayoutData(data
);
1001 * Locates the current part and selection and fires
1002 * {@link ISelectionListener#selectionChanged(IWorkbenchPart, ISelection)}
1003 * on the passed listener.
1005 * @param serviceLocator
1006 * @param selectionListener
1008 public static void notifySelectionChangedWithCurrentSelection(
1009 ISelectionListener selectionListener
, IServiceLocator serviceLocator
) {
1010 IHandlerService handlerService
= CommonUtils
.getService(serviceLocator
, IHandlerService
.class);
1011 IEvaluationContext state
= handlerService
.getCurrentState();
1012 // This seems to be the most reliable way to get the active part, it
1013 // also returns a part when it is called while creating a view that is
1014 // being shown.Getting the active part through the active workbench
1015 // window returned null in that case.
1016 Object partObject
= state
.getVariable(ISources
.ACTIVE_PART_NAME
);
1017 Object selectionObject
= state
1018 .getVariable(ISources
.ACTIVE_CURRENT_SELECTION_NAME
);
1019 if (partObject
instanceof IWorkbenchPart
1020 && selectionObject
instanceof ISelection
) {
1021 IWorkbenchPart part
= (IWorkbenchPart
) partObject
;
1022 ISelection selection
= (ISelection
) selectionObject
;
1023 if (!selection
.isEmpty())
1024 selectionListener
.selectionChanged(part
, selection
);