1 /*******************************************************************************
2 * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * See LICENSE for the full license text, also available.
7 *******************************************************************************/
8 package org
.eclipse
.egit
.ui
.internal
.components
;
10 import java
.io
.IOException
;
11 import java
.net
.URISyntaxException
;
12 import java
.util
.ArrayList
;
13 import java
.util
.Collection
;
14 import java
.util
.Collections
;
15 import java
.util
.Comparator
;
16 import java
.util
.HashMap
;
17 import java
.util
.HashSet
;
18 import java
.util
.LinkedList
;
19 import java
.util
.List
;
22 import java
.util
.TreeSet
;
23 import java
.util
.regex
.Pattern
;
25 import org
.eclipse
.core
.runtime
.IStatus
;
26 import org
.eclipse
.core
.runtime
.Status
;
27 import org
.eclipse
.egit
.ui
.Activator
;
28 import org
.eclipse
.egit
.ui
.UIIcons
;
29 import org
.eclipse
.egit
.ui
.UIText
;
30 import org
.eclipse
.jface
.dialogs
.ErrorDialog
;
31 import org
.eclipse
.jface
.fieldassist
.ComboContentAdapter
;
32 import org
.eclipse
.jface
.fieldassist
.ContentProposalAdapter
;
33 import org
.eclipse
.jface
.fieldassist
.ControlDecoration
;
34 import org
.eclipse
.jface
.fieldassist
.FieldDecorationRegistry
;
35 import org
.eclipse
.jface
.fieldassist
.IContentProposal
;
36 import org
.eclipse
.jface
.fieldassist
.IContentProposalListener
;
37 import org
.eclipse
.jface
.fieldassist
.IContentProposalProvider
;
38 import org
.eclipse
.jface
.fieldassist
.TextContentAdapter
;
39 import org
.eclipse
.jface
.layout
.TableColumnLayout
;
40 import org
.eclipse
.jface
.resource
.ImageRegistry
;
41 import org
.eclipse
.jface
.viewers
.CellEditor
;
42 import org
.eclipse
.jface
.viewers
.CheckboxCellEditor
;
43 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
44 import org
.eclipse
.jface
.viewers
.ColumnViewerToolTipSupport
;
45 import org
.eclipse
.jface
.viewers
.ColumnWeightData
;
46 import org
.eclipse
.jface
.viewers
.EditingSupport
;
47 import org
.eclipse
.jface
.viewers
.IElementComparer
;
48 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
49 import org
.eclipse
.jface
.viewers
.TableViewer
;
50 import org
.eclipse
.jface
.viewers
.TableViewerColumn
;
51 import org
.eclipse
.jface
.viewers
.TextCellEditor
;
52 import org
.eclipse
.jface
.viewers
.Viewer
;
53 import org
.eclipse
.osgi
.util
.NLS
;
54 import org
.eclipse
.swt
.SWT
;
55 import org
.eclipse
.swt
.events
.DisposeEvent
;
56 import org
.eclipse
.swt
.events
.DisposeListener
;
57 import org
.eclipse
.swt
.events
.ModifyEvent
;
58 import org
.eclipse
.swt
.events
.ModifyListener
;
59 import org
.eclipse
.swt
.events
.SelectionAdapter
;
60 import org
.eclipse
.swt
.events
.SelectionEvent
;
61 import org
.eclipse
.swt
.events
.TraverseEvent
;
62 import org
.eclipse
.swt
.events
.TraverseListener
;
63 import org
.eclipse
.swt
.graphics
.Color
;
64 import org
.eclipse
.swt
.graphics
.Image
;
65 import org
.eclipse
.swt
.layout
.GridData
;
66 import org
.eclipse
.swt
.layout
.GridLayout
;
67 import org
.eclipse
.swt
.layout
.RowLayout
;
68 import org
.eclipse
.swt
.widgets
.Button
;
69 import org
.eclipse
.swt
.widgets
.Combo
;
70 import org
.eclipse
.swt
.widgets
.Composite
;
71 import org
.eclipse
.swt
.widgets
.Control
;
72 import org
.eclipse
.swt
.widgets
.Group
;
73 import org
.eclipse
.swt
.widgets
.Label
;
74 import org
.eclipse
.swt
.widgets
.Table
;
75 import org
.eclipse
.swt
.widgets
.TableColumn
;
76 import org
.eclipse
.swt
.widgets
.Text
;
77 import org
.eclipse
.ui
.fieldassist
.ContentAssistCommandAdapter
;
78 import org
.spearce
.jgit
.lib
.Constants
;
79 import org
.spearce
.jgit
.lib
.ObjectId
;
80 import org
.spearce
.jgit
.lib
.Ref
;
81 import org
.spearce
.jgit
.lib
.Repository
;
82 import org
.spearce
.jgit
.lib
.Ref
.Storage
;
83 import org
.spearce
.jgit
.transport
.FetchConnection
;
84 import org
.spearce
.jgit
.transport
.RefSpec
;
85 import org
.spearce
.jgit
.transport
.RemoteConfig
;
86 import org
.spearce
.jgit
.transport
.Transport
;
89 * This class provides universal panel for editing list of {@link RefSpec} -
90 * specifications for both push or fetch, depending on panel configuration.
92 * It is intended to allow user easily edit specifications, supporting user with
93 * content assistant and giving feedback with error information as soon as
94 * possible. Component uses editable specifications table and panels for easy
95 * creation of new specifications basing on typical push/fetch schemes (like
96 * branch update, deletion, all branches update, saved configuration etc.).
98 * The model of specifications list behind panel is accessible by public methods
99 * - giving both read and write access. Listener interface for handling changes
100 * in model is provided by {@link SelectionChangeListener}.
102 * Typical class usage:
105 * // create panel for editing push-specifications
106 * RefSpecPanel panel = new RefSpecPanel(parent, true);
107 * // register model listener
108 * panel.addRefSpecPanelListener(listener);
110 * // provide information about local and remote refs
111 * panel.setRefsData(localRepo, remoteRefs, remoteName);
114 * List<RefSpec> result = panel.getRefSpecs();
115 * // further processing: push or save configuration...
118 * @see SelectionChangeListener
120 public class RefSpecPanel
{
121 private static final String IMAGE_ADD
= "ADD"; //$NON-NLS-1$
123 private static final String IMAGE_DELETE
= "DELETE"; //$NON-NLS-1$
125 private static final String IMAGE_TRASH
= "TRASH"; //$NON-NLS-1$
127 private static final String IMAGE_CLEAR
= "CLEAR"; //$NON-NLS-1$
129 private static final int TABLE_PREFERRED_HEIGHT
= 165;
131 private static final int TABLE_PREFERRED_WIDTH
= 560;
133 private static final int COLUMN_MODE_WEIGHT
= 23;
135 private static final int COLUMN_SRC_WEIGHT
= 40;
137 private static final int COLUMN_DST_WEIGHT
= 40;
139 private static final int COLUMN_FORCE_WEIGHT
= 30;
141 private static final int COLUMN_REMOVE_WEIGHT
= 20;
143 private static boolean isDeleteRefSpec(final Object element
) {
144 return ((RefSpec
) element
).getSource() == null;
147 private static boolean isValidRefExpression(final String s
) {
148 if (RefSpec
.isWildcard(s
)) {
149 // replace wildcard with some legal name just for checking
151 .isValidRefName(s
.substring(0, s
.length() - 1) + 'X');
153 return Repository
.isValidRefName(s
);
156 private static RefSpec
setRefSpecSource(final RefSpec spec
, final String src
) {
158 if (RefSpec
.isWildcard(src
))
159 dst
= wildcardSpecComponent(spec
.getDestination());
161 dst
= unwildcardSpecComponent(spec
.getDestination(), src
);
162 return spec
.setSourceDestination(src
, dst
);
165 private static RefSpec
setRefSpecDestination(final RefSpec spec
,
168 if (RefSpec
.isWildcard(dst
))
169 src
= wildcardSpecComponent(spec
.getSource());
171 src
= unwildcardSpecComponent(spec
.getSource(), dst
);
172 return spec
.setSourceDestination(src
, dst
);
175 private static String
wildcardSpecComponent(final String comp
) {
177 if (RefSpec
.isWildcard(comp
))
179 if (comp
== null || (i
= comp
.lastIndexOf('/')) == -1) {
180 // That's somewhat ugly. What better can we do here?
181 return UIText
.RefSpecPanel_refChooseSomeWildcard
;
183 return comp
.substring(0, i
+ 1) + '*';
186 private static String
unwildcardSpecComponent(final String comp
,
187 final String other
) {
188 if (!RefSpec
.isWildcard(comp
))
190 if (other
== null || other
.length() == 0)
191 return ""; //$NON-NLS-1$
192 final int i
= other
.lastIndexOf('/');
193 return comp
.substring(0, comp
.length() - 1) + other
.substring(i
+ 1);
196 private static List
<RefContentProposal
> createProposalsFilteredRemote(
197 final List
<RefContentProposal
> proposals
) {
198 final List
<RefContentProposal
> result
= new ArrayList
<RefContentProposal
>();
199 for (final RefContentProposal p
: proposals
) {
200 final String content
= p
.getContent();
201 if (content
.equals(Constants
.HEAD
)
202 || content
.startsWith(Constants
.R_HEADS
))
208 private static Image
getDecorationImage(final String key
) {
209 return FieldDecorationRegistry
.getDefault().getFieldDecoration(key
)
213 private static void setControlDecoration(final ControlDecoration control
,
214 final String imageKey
, final String description
) {
215 control
.setImage(getDecorationImage(imageKey
));
216 control
.setDescriptionText(description
);
220 private final List
<RefSpec
> specs
= new ArrayList
<RefSpec
>();
222 private final Composite panel
;
224 private TableViewer tableViewer
;
226 private CellEditor modeCellEditor
;
228 private CellEditor localRefCellEditor
;
230 private CellEditor remoteRefCellEditor
;
232 private CellEditor forceUpdateCellEditor
;
234 private CellEditor removeSpecCellEditor
;
236 private int srcColumnIndex
;
238 private Button removeAllSpecButton
;
240 private Button forceUpdateAllButton
;
242 private Button creationButton
;
244 private Button addConfiguredButton
;
246 private Button addTagsButton
;
248 private Button addBranchesButton
;
250 private ControlDecoration creationSrcDecoration
;
252 private ControlDecoration creationDstDecoration
;
254 private ControlDecoration deleteRefDecoration
;
256 private Combo creationSrcCombo
;
258 private Combo creationDstCombo
;
260 private Combo deleteRefCombo
;
262 private Button deleteButton
;
264 private Repository localDb
;
266 private String remoteName
;
268 private Set
<String
> localRefNames
= Collections
.emptySet();
270 private Set
<String
> remoteRefNames
= Collections
.emptySet();
272 private List
<RefSpec
> predefinedConfigured
= Collections
.emptyList();
274 private RefSpec predefinedBranches
= null;
276 private final RefContentProposalProvider remoteProposalProvider
;
278 private final RefContentProposalProvider localProposalProvider
;
280 private ComboLabelingSupport creationSrcComboSupport
;
282 private ComboLabelingSupport creationDstComboSupport
;
284 private ComboLabelingSupport deleteRefComboSupport
;
286 private final boolean pushSpecs
;
288 private final List
<SelectionChangeListener
> listeners
= new LinkedList
<SelectionChangeListener
>();
290 private final ImageRegistry imageRegistry
;
292 private boolean matchingAnyRefs
;
294 private RefSpec invalidSpec
;
296 private RefSpec invalidSpecSameDst
;
298 private String errorMessage
;
300 private Color errorBackgroundColor
;
302 private Color errorTextColor
;
305 * Create a new panel and install it on a provided composite. Panel is
306 * created either for editing push or fetch specifications - this setting
307 * can't be changed later, after constructing object.
309 * Panel is created with an empty model, with no provided assistant. It
310 * can't be used by user until
311 * {@link #setAssistanceData(Repository, Collection, String)} method is
312 * called, and to this time is disabled.
315 * parent control for panel.
317 * true if panel is used for editing push specifications, false
318 * if panel is used for editing fetch specifications.
320 public RefSpecPanel(final Composite parent
, final boolean pushSpecs
) {
321 this.pushSpecs
= pushSpecs
;
322 this.localProposalProvider
= new RefContentProposalProvider(pushSpecs
);
323 this.remoteProposalProvider
= new RefContentProposalProvider(false);
324 this.imageRegistry
= new ImageRegistry(parent
.getDisplay());
326 panel
= new Composite(parent
, SWT
.NONE
);
327 panel
.setLayout(new GridLayout());
329 safeCreateResources();
331 createCreationPanel();
333 createDeleteCreationPanel();
334 createPredefinedCreationPanel();
337 addRefSpecTableListener(new SelectionChangeListener() {
338 public void selectionChanged() {
346 * Enable or disable panel controls.
349 * true to enable panel, false to disable.
351 public void setEnable(final boolean enable
) {
352 getControl().setEnabled(enable
);
356 * Set information needed for assisting user with entering data and
357 * validating user input. This method automatically enables the panel.
360 * local repository where specifications will be applied.
362 * collection of remote refs as advertised by remote repository.
363 * Typically they are collected by {@link FetchConnection}
366 * optional name for remote configuration, if edited
367 * specification list is related to this remote configuration.
368 * Can be null. When not null, panel is filled with default
369 * fetch/push specifications for this remote configuration.
371 public void setAssistanceData(final Repository localRepo
,
372 final Collection
<Ref
> remoteRefs
, final String remoteName
) {
373 this.localDb
= localRepo
;
374 this.remoteName
= remoteName
;
376 final List
<RefContentProposal
> remoteProposals
= createContentProposals(
378 remoteProposalProvider
.setProposals(remoteProposals
);
379 remoteRefNames
= new HashSet
<String
>();
380 for (final RefContentProposal p
: remoteProposals
)
381 remoteRefNames
.add(p
.getContent());
385 final ObjectId id
= localDb
.resolve(Constants
.HEAD
);
387 HEAD
= new Ref(Storage
.LOOSE
, Constants
.HEAD
, id
);
388 } catch (IOException e
) {
389 Activator
.logError("Couldn't read HEAD from local repository", e
); //$NON-NLS-1$
391 final List
<RefContentProposal
> localProposals
= createContentProposals(
392 localDb
.getAllRefs().values(), HEAD
);
393 localProposalProvider
.setProposals(localProposals
);
394 localRefNames
= new HashSet
<String
>();
395 for (final RefContentProposal ref
: localProposals
)
396 localRefNames
.add(ref
.getContent());
398 final List
<RefContentProposal
> localFilteredProposals
= createProposalsFilteredLocal(localProposals
);
399 final List
<RefContentProposal
> remoteFilteredProposals
= createProposalsFilteredRemote(remoteProposals
);
402 creationSrcComboSupport
.setProposals(localFilteredProposals
);
403 creationDstComboSupport
.setProposals(remoteFilteredProposals
);
405 creationSrcComboSupport
.setProposals(remoteFilteredProposals
);
406 creationDstComboSupport
.setProposals(localFilteredProposals
);
408 validateCreationPanel();
411 deleteRefComboSupport
.setProposals(remoteFilteredProposals
);
412 validateDeleteCreationPanel();
416 if (remoteName
== null)
417 predefinedConfigured
= Collections
.emptyList();
419 final RemoteConfig rc
= new RemoteConfig(localDb
.getConfig(),
422 predefinedConfigured
= rc
.getPushRefSpecs();
424 predefinedConfigured
= rc
.getFetchRefSpecs();
425 for (final RefSpec spec
: predefinedConfigured
)
428 } catch (URISyntaxException e
) {
429 predefinedConfigured
= null;
430 ErrorDialog
.openError(panel
.getShell(),
431 UIText
.RefSpecPanel_errorRemoteConfigTitle
,
432 UIText
.RefSpecPanel_errorRemoteConfigDescription
,
433 new Status(IStatus
.ERROR
, Activator
.getPluginId(), 0, e
436 updateAddPredefinedButton(addConfiguredButton
, predefinedConfigured
);
438 predefinedBranches
= Transport
.REFSPEC_PUSH_ALL
;
441 if (remoteName
== null)
442 r
= UIText
.RefSpecPanel_refChooseRemoteName
;
445 predefinedBranches
= new RefSpec("refs/heads/*:refs/remotes/" //$NON-NLS-1$
446 + r
+ "/*"); //$NON-NLS-1$
448 updateAddPredefinedButton(addBranchesButton
, predefinedBranches
);
453 * @return underlying control for this panel.
455 public Control
getControl() {
460 * Return current list of specifications of this panel.
462 * This method should be called only from the UI thread.
464 * @return unmodifiable view of specifications list as edited by user in
465 * this panel. Note that this view underlying model may change -
466 * create a copy if needed.
468 public List
<RefSpec
> getRefSpecs() {
469 return Collections
.unmodifiableList(specs
);
473 * @return true if specifications list is empty, false otherwise.
475 public boolean isEmpty() {
476 return getRefSpecs().isEmpty();
480 * @return true if specifications match any ref(s) in source repository -
481 * resolve to concrete ref updates, false otherwise. For non empty
482 * specifications list, false value is possible only in case of
483 * specifications with wildcards.
485 public boolean isMatchingAnyRefs() {
486 return matchingAnyRefs
;
490 * Add provided specification to this panel. Panel view is automatically
491 * refreshed, model is revalidated.
493 * Note that the same reference can't be added twice to the panel, while two
494 * or more equals RefSpec (in terms of equals method) can be - likely
495 * causing validation error.
497 * This method should be called only from the UI thread.
500 * specification to add.
501 * @throws IllegalArgumentException
502 * if specification with same reference already exists in panel.
504 public void addRefSpec(final RefSpec spec
) {
505 final int i
= indexOfSpec(spec
);
507 throw new IllegalArgumentException("RefSpec " + spec
//$NON-NLS-1$
508 + " already exists."); //$NON-NLS-1$
510 tableViewer
.add(spec
);
511 notifySpecsChanged();
515 * Remove provided specification from this panel. Panel view is
516 * automatically refreshed, model is revalidated.
518 * Provided specification must be equals with existing one in terms of
519 * reference equality, not an equals method.
521 * This method should be called only from the UI thread.
524 * specification to remove.
525 * @throws IllegalArgumentException
526 * if specification with this reference doesn't exist in this
529 public void removeRefSpec(final RefSpec spec
) {
530 final int i
= indexOfSpec(spec
);
532 throw new IllegalArgumentException("RefSpec " + spec
//$NON-NLS-1$
533 + " not found."); //$NON-NLS-1$
535 tableViewer
.remove(spec
);
536 notifySpecsChanged();
540 * Change some specification to the new one.
542 * Old specification must exist in the panel, while new specification can't
543 * exist before (both in terms of reference equality).
545 * This method should be called only from the UI thread.
548 * specification to change. Can't be null.
550 * new specification to override existing one. Can't be null.
552 public void setRefSpec(final RefSpec oldSpec
, final RefSpec newSpec
) {
553 final int oldI
= indexOfSpec(oldSpec
);
555 throw new IllegalArgumentException("RefSpec " + oldSpec
//$NON-NLS-1$
556 + " not found."); //$NON-NLS-1$
557 final int newI
= indexOfSpec(newSpec
);
559 throw new IllegalArgumentException("RefSpec " + newSpec
//$NON-NLS-1$
560 + " already exists."); //$NON-NLS-1$
561 specs
.set(oldI
, newSpec
);
563 // have to refresh whole table as we are operating on immutable objects
564 // (this shouldn't be an issue)
565 tableViewer
.refresh();
566 notifySpecsChanged();
570 * Clear all specifications from this panel.
572 * This method should be called only from the UI thread.
574 public void clearRefSpecs() {
575 final RefSpec toRemove
[] = specs
.toArray(new RefSpec
[0]);
577 tableViewer
.remove(toRemove
);
578 notifySpecsChanged();
582 * Add listener of changes in panel model.
584 * Listeners are notified on events caused by both operations invoked by
585 * external calls and user interaction. Listener method(s) is always called
586 * from UI thread and shouldn't perform long computations.
588 * Order of adding listeners is significant. This method is not thread-safe.
589 * Listeners should be set up before panel usage.
594 public void addRefSpecTableListener(final SelectionChangeListener listener
) {
595 listeners
.add(listener
);
599 * Get user-friendly error message regarding invalid specification.
601 * @return user-readable information about invalid specification.
603 public String
getErrorMessage() {
608 * Return information about validity of specifications.
610 * Specifications are considered valid if pushing/fetching (depending on
611 * panel configuration) shouldn't cause any error except for
612 * non-fast-forward or server-related errors complaint. I.e. specifications
613 * destinations don't overlap and every specification is correctly
614 * formulated, preferably none is referring to non-existing ref etc.
616 * @return true if all specifications in panel are valid, false if at least
617 * one specification is invalid (in this case
618 * {@link #getErrorMessage()} gives detailed information for user).
620 public boolean isValid() {
621 return errorMessage
== null;
624 private int indexOfSpec(final RefSpec spec
) {
626 for (i
= 0; i
< specs
.size(); i
++) {
627 // we have to compare references, not use List#indexOf,
628 // as equals is implemented in RefSpec
629 if (specs
.get(i
) == spec
)
632 if (i
== specs
.size())
637 private void notifySpecsChanged() {
638 for (final SelectionChangeListener listener
: listeners
)
639 listener
.selectionChanged();
642 private void safeCreateResources() {
643 imageRegistry
.put(IMAGE_ADD
, UIIcons
.ELCL16_ADD
);
644 imageRegistry
.put(IMAGE_DELETE
, UIIcons
.ELCL16_DELETE
);
645 imageRegistry
.put(IMAGE_TRASH
, UIIcons
.ELCL16_TRASH
);
646 imageRegistry
.put(IMAGE_CLEAR
, UIIcons
.ELCL16_CLEAR
);
647 errorBackgroundColor
= new Color(panel
.getDisplay(), 255, 150, 150);
648 errorTextColor
= new Color(panel
.getDisplay(), 255, 0, 0);
650 panel
.addDisposeListener(new DisposeListener() {
651 public void widgetDisposed(DisposeEvent e
) {
652 imageRegistry
.dispose();
653 errorBackgroundColor
.dispose();
654 errorTextColor
.dispose();
659 private RefContentProposalProvider
getRefsProposalProvider(
660 final boolean local
) {
661 return (local ? localProposalProvider
: remoteProposalProvider
);
664 private void createCreationPanel() {
665 final Group creationPanel
= new Group(panel
, SWT
.NONE
);
666 creationPanel
.setText(UIText
.RefSpecPanel_creationGroup
);
667 creationPanel
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true,
669 final GridLayout layout
= new GridLayout();
670 layout
.numColumns
= 3;
671 layout
.horizontalSpacing
= 10;
672 layout
.verticalSpacing
= 2;
673 creationPanel
.setLayout(layout
);
675 new Label(creationPanel
, SWT
.NONE
)
676 .setText(UIText
.RefSpecPanel_creationSrc
);
677 new Label(creationPanel
, SWT
.NONE
)
678 .setText(UIText
.RefSpecPanel_creationDst
);
679 creationButton
= new Button(creationPanel
, SWT
.PUSH
);
680 creationButton
.setLayoutData(new GridData(SWT
.RIGHT
, SWT
.BOTTOM
, false,
682 creationButton
.setImage(imageRegistry
.get(IMAGE_ADD
));
683 creationButton
.setText(UIText
.RefSpecPanel_creationButton
);
684 creationButton
.addSelectionListener(new SelectionAdapter() {
686 public void widgetSelected(SelectionEvent e
) {
687 final String src
= creationSrcCombo
.getText();
688 final String dst
= creationDstCombo
.getText();
689 RefSpec spec
= new RefSpec(src
+ ':' + dst
);
691 creationSrcCombo
.setText(""); //$NON-NLS-1$
692 creationDstCombo
.setText(""); //$NON-NLS-1$
695 creationButton
.setToolTipText(NLS
.bind(
696 UIText
.RefSpecPanel_creationButtonDescription
, typeString()));
698 creationSrcDecoration
= createAssistedDecoratedCombo(creationPanel
,
699 getRefsProposalProvider(pushSpecs
),
700 new IContentProposalListener() {
701 public void proposalAccepted(IContentProposal proposal
) {
702 tryAutoCompleteSrcToDst();
705 creationSrcCombo
= (Combo
) creationSrcDecoration
.getControl();
706 creationSrcCombo
.setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
, true,
708 creationSrcCombo
.addTraverseListener(new TraverseListener() {
709 public void keyTraversed(TraverseEvent e
) {
710 // SWT.TRAVERSE_RETURN may be also reasonable here, but
711 // it can be confused with RETURN for content proposal
712 if (e
.detail
== SWT
.TRAVERSE_TAB_NEXT
)
713 tryAutoCompleteSrcToDst();
718 .setToolTipText(UIText
.RefSpecPanel_srcPushDescription
);
721 .setToolTipText(UIText
.RefSpecPanel_srcFetchDescription
);
722 creationSrcComboSupport
= new ComboLabelingSupport(creationSrcCombo
,
723 new SelectionAdapter() {
725 public void widgetSelected(SelectionEvent e
) {
726 tryAutoCompleteSrcToDst();
730 creationDstDecoration
= createAssistedDecoratedCombo(creationPanel
,
731 getRefsProposalProvider(!pushSpecs
),
732 new IContentProposalListener() {
733 public void proposalAccepted(IContentProposal proposal
) {
734 tryAutoCompleteDstToSrc();
737 creationDstCombo
= (Combo
) creationDstDecoration
.getControl();
738 creationDstCombo
.setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
, true,
740 creationDstCombo
.addTraverseListener(new TraverseListener() {
741 public void keyTraversed(TraverseEvent e
) {
742 // SWT.TRAVERSE_RETURN may be also reasonable here, but
743 // it can be confused with RETURN for content proposal
744 if (e
.detail
== SWT
.TRAVERSE_TAB_NEXT
)
745 tryAutoCompleteDstToSrc();
750 .setToolTipText(UIText
.RefSpecPanel_dstPushDescription
);
753 .setToolTipText(UIText
.RefSpecPanel_dstFetchDescription
);
754 creationDstComboSupport
= new ComboLabelingSupport(creationDstCombo
,
755 new SelectionAdapter() {
757 public void widgetSelected(SelectionEvent e
) {
758 tryAutoCompleteDstToSrc();
762 validateCreationPanel();
763 final ModifyListener validator
= new ModifyListener() {
764 public void modifyText(final ModifyEvent e
) {
765 validateCreationPanel();
768 creationSrcCombo
.addModifyListener(validator
);
769 creationDstCombo
.addModifyListener(validator
);
772 private void createDeleteCreationPanel() {
773 final Group deletePanel
= new Group(panel
, SWT
.NONE
);
774 deletePanel
.setText(UIText
.RefSpecPanel_deletionGroup
);
776 .setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, false));
777 final GridLayout layout
= new GridLayout();
778 layout
.numColumns
= 3;
779 layout
.horizontalSpacing
= 10;
780 deletePanel
.setLayout(layout
);
782 final Label label
= new Label(deletePanel
, SWT
.NONE
);
783 label
.setText(UIText
.RefSpecPanel_deletionRef
);
784 label
.setLayoutData(new GridData(SWT
.RIGHT
, SWT
.CENTER
, false, false));
786 deleteRefDecoration
= createAssistedDecoratedCombo(deletePanel
,
787 getRefsProposalProvider(false), null);
788 deleteRefCombo
= (Combo
) deleteRefDecoration
.getControl();
789 deleteRefCombo
.setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
, true,
792 .setToolTipText(UIText
.RefSpecPanel_dstDeletionDescription
);
793 deleteRefComboSupport
= new ComboLabelingSupport(deleteRefCombo
, null);
795 deleteButton
= new Button(deletePanel
, SWT
.PUSH
);
796 deleteButton
.setLayoutData(new GridData(SWT
.RIGHT
, SWT
.CENTER
, false,
798 deleteButton
.setImage(imageRegistry
.get(IMAGE_DELETE
));
799 deleteButton
.setText(UIText
.RefSpecPanel_deletionButton
);
800 deleteButton
.addSelectionListener(new SelectionAdapter() {
802 public void widgetSelected(SelectionEvent e
) {
803 RefSpec spec
= new RefSpec(':' + deleteRefCombo
.getText());
805 deleteRefCombo
.setText(""); //$NON-NLS-1$
809 .setToolTipText(UIText
.RefSpecPanel_deletionButtonDescription
);
810 validateDeleteCreationPanel();
812 deleteRefCombo
.addModifyListener(new ModifyListener() {
813 public void modifyText(final ModifyEvent e
) {
814 validateDeleteCreationPanel();
819 private void createPredefinedCreationPanel() {
820 final Group predefinedPanel
= new Group(panel
, SWT
.NONE
);
821 predefinedPanel
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true,
823 predefinedPanel
.setText(UIText
.RefSpecPanel_predefinedGroup
);
824 final GridLayout layout
= new GridLayout();
825 layout
.numColumns
= 3;
826 predefinedPanel
.setLayout(layout
);
828 addConfiguredButton
= new Button(predefinedPanel
, SWT
.PUSH
);
829 addConfiguredButton
.setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
,
831 addConfiguredButton
.setText(NLS
.bind(
832 UIText
.RefSpecPanel_predefinedConfigured
, typeString()));
833 addConfiguredButton
.addSelectionListener(new SelectionAdapter() {
835 public void widgetSelected(SelectionEvent e
) {
836 addPredefinedRefSpecs(predefinedConfigured
);
840 .setToolTipText(UIText
.RefSpecPanel_predefinedConfiguredDescription
);
841 updateAddPredefinedButton(addConfiguredButton
, predefinedConfigured
);
843 addBranchesButton
= new Button(predefinedPanel
, SWT
.PUSH
);
844 addBranchesButton
.setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
,
846 addBranchesButton
.setText(UIText
.RefSpecPanel_predefinedAll
);
847 addBranchesButton
.addSelectionListener(new SelectionAdapter() {
849 public void widgetSelected(SelectionEvent e
) {
850 addPredefinedRefSpecs(predefinedBranches
);
854 .setToolTipText(UIText
.RefSpecPanel_predefinedAllDescription
);
855 updateAddPredefinedButton(addBranchesButton
, predefinedBranches
);
857 addTagsButton
= new Button(predefinedPanel
, SWT
.PUSH
);
858 addTagsButton
.setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
, true,
860 addTagsButton
.setText(UIText
.RefSpecPanel_predefinedTags
);
861 addTagsButton
.addSelectionListener(new SelectionAdapter() {
863 public void widgetSelected(SelectionEvent e
) {
864 addPredefinedRefSpecs(Transport
.REFSPEC_TAGS
);
868 .setToolTipText(UIText
.RefSpecPanel_predefinedTagsDescription
);
869 updateAddPredefinedButton(addTagsButton
, Transport
.REFSPEC_TAGS
);
871 addRefSpecTableListener(new SelectionChangeListener() {
872 public void selectionChanged() {
873 updateAddPredefinedButton(addConfiguredButton
,
874 predefinedConfigured
);
875 updateAddPredefinedButton(addBranchesButton
, predefinedBranches
);
876 updateAddPredefinedButton(addTagsButton
, Transport
.REFSPEC_TAGS
);
881 private ControlDecoration
createAssistedDecoratedCombo(
882 final Composite parent
,
883 final IContentProposalProvider proposalProvider
,
884 final IContentProposalListener listener
) {
885 // FIXME: VERY ANNOYING! reported as 243991 in eclipse bugzilla
886 // when typing, pressing arrow-down key opens combo box drop-down
887 // instead of moving within autocompletion list (Mac 10.4&10.5, Eclipse
889 final Combo combo
= new Combo(parent
, SWT
.DROP_DOWN
);
890 final ControlDecoration decoration
= new ControlDecoration(combo
,
891 SWT
.BOTTOM
| SWT
.LEFT
);
892 final ContentAssistCommandAdapter proposal
= new ContentAssistCommandAdapter(
893 combo
, new ComboContentAdapter(), proposalProvider
, null, null,
896 .setProposalAcceptanceStyle(ContentProposalAdapter
.PROPOSAL_REPLACE
);
897 if (listener
!= null)
898 proposal
.addContentProposalListener(listener
);
902 private void createTableGroup() {
903 final Group tableGroup
= new Group(panel
, SWT
.NONE
);
904 tableGroup
.setText(NLS
.bind(UIText
.RefSpecPanel_specifications
,
906 tableGroup
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
907 tableGroup
.setLayout(new GridLayout());
909 createTable(tableGroup
);
910 createSpecsButtonsPanel(tableGroup
);
913 private void createTable(final Group tableGroup
) {
914 final Composite tablePanel
= new Composite(tableGroup
, SWT
.NONE
);
915 final GridData layoutData
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
916 layoutData
.heightHint
= TABLE_PREFERRED_HEIGHT
;
917 layoutData
.widthHint
= TABLE_PREFERRED_WIDTH
;
918 tablePanel
.setLayoutData(layoutData
);
920 tableViewer
= new TableViewer(tablePanel
, SWT
.FULL_SELECTION
921 | SWT
.MULTI
| SWT
.BORDER
| SWT
.V_SCROLL
);
922 ColumnViewerToolTipSupport
.enableFor(tableViewer
);
923 final Table table
= tableViewer
.getTable();
924 table
.setLinesVisible(true);
925 table
.setHeaderVisible(true);
927 createTableColumns(tablePanel
);
928 createCellEditors(table
);
930 tableViewer
.setContentProvider(new IStructuredContentProvider() {
931 public Object
[] getElements(final Object inputElement
) {
932 return ((List
) inputElement
).toArray();
935 public void dispose() {
936 // nothing to dispose
939 public void inputChanged(Viewer viewer
, Object oldInput
,
941 // input is hard coded
944 tableViewer
.setInput(specs
);
946 tableViewer
.setComparer(new IElementComparer() {
947 public boolean equals(Object a
, Object b
) {
948 // need that as viewers are not designed to support 2 equals
949 // object, while we have RefSpec#equals implemented
953 public int hashCode(Object element
) {
954 return element
.hashCode();
959 private void createTableColumns(final Composite tablePanel
) {
960 final TableColumnLayout columnLayout
= new TableColumnLayout();
961 tablePanel
.setLayout(columnLayout
);
963 createDummyColumn(columnLayout
);
965 createModeColumn(columnLayout
);
966 createSrcColumn(columnLayout
);
967 createDstColumn(columnLayout
);
968 createForceColumn(columnLayout
);
969 createRemoveColumn(columnLayout
);
972 private void createDummyColumn(final TableColumnLayout columnLayout
) {
973 final TableViewerColumn viewerColumn
= new TableViewerColumn(
974 tableViewer
, SWT
.LEFT
);
975 final TableColumn column
= viewerColumn
.getColumn();
976 columnLayout
.setColumnData(column
, new ColumnWeightData(0, 0, false));
977 viewerColumn
.setLabelProvider(new ColumnLabelProvider());
978 // FIXME: first cell is left aligned on Mac OS X 10.4, Eclipse 3.4
981 private void createModeColumn(final TableColumnLayout columnLayout
) {
982 final TableViewerColumn column
= createColumn(columnLayout
,
983 UIText
.RefSpecPanel_columnMode
, COLUMN_MODE_WEIGHT
, SWT
.CENTER
);
984 column
.setLabelProvider(new ColumnLabelProvider() {
986 public String
getText(final Object element
) {
987 return (isDeleteRefSpec(element
) ? UIText
.RefSpecPanel_modeDelete
988 : UIText
.RefSpecPanel_modeUpdate
);
992 public Image
getImage(Object element
) {
993 return (isDeleteRefSpec(element
) ? imageRegistry
994 .get(IMAGE_DELETE
) : imageRegistry
.get(IMAGE_ADD
));
998 public String
getToolTipText(Object element
) {
999 if (isDeleteRefSpec(element
))
1000 return UIText
.RefSpecPanel_modeDeleteDescription
+ '\n'
1001 + UIText
.RefSpecPanel_clickToChange
;
1002 return UIText
.RefSpecPanel_modeUpdateDescription
+ '\n'
1003 + UIText
.RefSpecPanel_clickToChange
;
1006 column
.setEditingSupport(new EditingSupport(tableViewer
) {
1008 protected boolean canEdit(final Object element
) {
1013 protected CellEditor
getCellEditor(final Object element
) {
1014 return modeCellEditor
;
1018 protected Object
getValue(final Object element
) {
1019 return isDeleteRefSpec(element
);
1023 protected void setValue(final Object element
, final Object value
) {
1024 final RefSpec oldSpec
= (RefSpec
) element
;
1025 final RefSpec newSpec
;
1026 if ((Boolean
) value
) {
1027 newSpec
= setRefSpecSource(oldSpec
, null);
1028 setRefSpec(oldSpec
, newSpec
);
1030 newSpec
= setRefSpecSource(oldSpec
,
1031 UIText
.RefSpecPanel_refChooseSome
);
1032 setRefSpec(oldSpec
, newSpec
);
1033 tableViewer
.getControl().getDisplay().asyncExec(
1036 tableViewer
.editElement(newSpec
,
1045 private void createSrcColumn(final TableColumnLayout columnLayout
) {
1046 final TableViewerColumn column
= createColumn(columnLayout
,
1047 UIText
.RefSpecPanel_columnSrc
, COLUMN_SRC_WEIGHT
, SWT
.LEFT
);
1048 column
.setLabelProvider(new ColumnLabelProvider() {
1050 public String
getText(final Object element
) {
1051 return ((RefSpec
) element
).getSource();
1055 public String
getToolTipText(Object element
) {
1056 if (isInvalidSpec(element
))
1057 return errorMessage
;
1058 if (isDeleteRefSpec(element
))
1059 return UIText
.RefSpecPanel_srcDeleteDescription
;
1061 return UIText
.RefSpecPanel_srcPushDescription
;
1062 return UIText
.RefSpecPanel_srcFetchDescription
;
1066 public Color
getBackground(final Object element
) {
1067 if (isInvalidSpec(element
))
1068 return errorBackgroundColor
;
1073 public Color
getToolTipForegroundColor(Object element
) {
1074 if (isInvalidSpec(element
))
1075 return errorTextColor
;
1079 column
.setEditingSupport(new EditingSupport(tableViewer
) {
1081 protected boolean canEdit(final Object element
) {
1082 return !isDeleteRefSpec(element
);
1086 protected CellEditor
getCellEditor(final Object element
) {
1087 return (pushSpecs ? localRefCellEditor
: remoteRefCellEditor
);
1091 protected Object
getValue(final Object element
) {
1092 return ((RefSpec
) element
).getSource();
1096 protected void setValue(final Object element
, final Object value
) {
1097 if (value
== null || ((String
) value
).length() == 0
1098 || ObjectId
.zeroId().name().equals(value
)) {
1099 // Ignore empty strings or null objects - do not set them in
1100 // model.User won't loose any information if we just fall
1101 // back to the old value.
1102 // If user want to delete ref, let change the mode.
1106 final RefSpec oldSpec
= (RefSpec
) element
;
1107 final RefSpec newSpec
= setRefSpecSource(oldSpec
,
1109 setRefSpec(oldSpec
, newSpec
);
1113 // find index of this column - for later usage
1114 final TableColumn
[] columns
= tableViewer
.getTable().getColumns();
1115 for (srcColumnIndex
= 0; srcColumnIndex
< columns
.length
; srcColumnIndex
++)
1116 if (columns
[srcColumnIndex
] == column
.getColumn())
1121 private void createDstColumn(final TableColumnLayout columnLayout
) {
1122 final TableViewerColumn column
= createColumn(columnLayout
,
1123 UIText
.RefSpecPanel_columnDst
, COLUMN_DST_WEIGHT
, SWT
.LEFT
);
1124 column
.setLabelProvider(new ColumnLabelProvider() {
1126 public String
getText(final Object element
) {
1127 return ((RefSpec
) element
).getDestination();
1131 public String
getToolTipText(Object element
) {
1132 if (isInvalidSpec(element
))
1133 return errorMessage
;
1134 if (isDeleteRefSpec(element
))
1135 return UIText
.RefSpecPanel_dstDeletionDescription
;
1137 return UIText
.RefSpecPanel_dstPushDescription
;
1138 return UIText
.RefSpecPanel_dstFetchDescription
;
1142 public Color
getBackground(final Object element
) {
1143 if (isInvalidSpec(element
))
1144 return errorBackgroundColor
;
1149 public Color
getToolTipForegroundColor(Object element
) {
1150 if (isInvalidSpec(element
))
1151 return errorTextColor
;
1155 column
.setEditingSupport(new EditingSupport(tableViewer
) {
1157 protected boolean canEdit(final Object element
) {
1162 protected CellEditor
getCellEditor(final Object element
) {
1163 return (pushSpecs ? remoteRefCellEditor
: localRefCellEditor
);
1167 protected Object
getValue(final Object element
) {
1168 return ((RefSpec
) element
).getDestination();
1172 protected void setValue(final Object element
, final Object value
) {
1173 if (value
== null || ((String
) value
).length() == 0) {
1174 // Ignore empty strings - do not set them in model.
1175 // User won't loose any information if we just fall back
1176 // to the old value.
1180 final RefSpec oldSpec
= (RefSpec
) element
;
1181 final RefSpec newSpec
= setRefSpecDestination(oldSpec
,
1183 setRefSpec(oldSpec
, newSpec
);
1188 private void createForceColumn(final TableColumnLayout columnLayout
) {
1189 final TableViewerColumn column
= createColumn(columnLayout
,
1190 UIText
.RefSpecPanel_columnForce
, COLUMN_FORCE_WEIGHT
,
1192 column
.setLabelProvider(new CheckboxLabelProvider(tableViewer
1195 protected boolean isChecked(final Object element
) {
1196 return ((RefSpec
) element
).isForceUpdate();
1200 protected boolean isEnabled(Object element
) {
1201 return !isDeleteRefSpec(element
);
1205 public String
getToolTipText(Object element
) {
1206 if (!isEnabled(element
))
1207 return UIText
.RefSpecPanel_forceDeleteDescription
;
1208 if (isChecked(element
))
1209 return UIText
.RefSpecPanel_forceTrueDescription
+ '\n'
1210 + UIText
.RefSpecPanel_clickToChange
;
1211 return UIText
.RefSpecPanel_forceFalseDescription
+ '\n'
1212 + UIText
.RefSpecPanel_clickToChange
;
1215 column
.setEditingSupport(new EditingSupport(tableViewer
) {
1217 protected boolean canEdit(final Object element
) {
1218 return !isDeleteRefSpec(element
);
1222 protected CellEditor
getCellEditor(final Object element
) {
1223 return forceUpdateCellEditor
;
1227 protected Object
getValue(final Object element
) {
1228 return ((RefSpec
) element
).isForceUpdate();
1232 protected void setValue(final Object element
, final Object value
) {
1233 final RefSpec oldSpec
= (RefSpec
) element
;
1234 final RefSpec newSpec
= oldSpec
.setForceUpdate((Boolean
) value
);
1235 setRefSpec(oldSpec
, newSpec
);
1240 private void createRemoveColumn(TableColumnLayout columnLayout
) {
1241 final TableViewerColumn column
= createColumn(columnLayout
,
1242 UIText
.RefSpecPanel_columnRemove
, COLUMN_REMOVE_WEIGHT
,
1244 column
.setLabelProvider(new CenteredImageLabelProvider() {
1246 public Image
getImage(Object element
) {
1247 return imageRegistry
.get(IMAGE_TRASH
);
1251 public String
getToolTipText(Object element
) {
1252 return NLS
.bind(UIText
.RefSpecPanel_removeDescription
,
1256 column
.setEditingSupport(new EditingSupport(tableViewer
) {
1258 protected boolean canEdit(Object element
) {
1263 protected CellEditor
getCellEditor(Object element
) {
1264 return removeSpecCellEditor
;
1268 protected Object
getValue(Object element
) {
1273 protected void setValue(Object element
, Object value
) {
1274 removeRefSpec((RefSpec
) element
);
1279 private TableViewerColumn
createColumn(
1280 final TableColumnLayout columnLayout
, final String text
,
1281 final int weight
, final int style
) {
1282 final TableViewerColumn viewerColumn
= new TableViewerColumn(
1283 tableViewer
, style
);
1284 final TableColumn column
= viewerColumn
.getColumn();
1285 column
.setText(text
);
1286 columnLayout
.setColumnData(column
, new ColumnWeightData(weight
));
1287 return viewerColumn
;
1290 private void createCellEditors(final Table table
) {
1292 modeCellEditor
= new CheckboxCellEditor(table
);
1293 localRefCellEditor
= createLocalRefCellEditor(table
);
1294 remoteRefCellEditor
= createRemoteRefCellEditor(table
);
1295 forceUpdateCellEditor
= new CheckboxCellEditor(table
);
1296 removeSpecCellEditor
= new ClickableCellEditor(table
);
1299 private CellEditor
createLocalRefCellEditor(final Table table
) {
1300 return createRefCellEditor(table
, getRefsProposalProvider(true));
1303 private CellEditor
createRemoteRefCellEditor(final Table table
) {
1304 return createRefCellEditor(table
, getRefsProposalProvider(false));
1307 private CellEditor
createRefCellEditor(final Table table
,
1308 final IContentProposalProvider proposalProvider
) {
1309 final CellEditor cellEditor
= new TextCellEditor(table
);
1311 final Text text
= (Text
) cellEditor
.getControl();
1312 final ContentAssistCommandAdapter assist
= new ContentAssistCommandAdapter(
1313 text
, new TextContentAdapter(), proposalProvider
, null, null,
1316 .setProposalAcceptanceStyle(ContentProposalAdapter
.PROPOSAL_REPLACE
);
1321 private void createSpecsButtonsPanel(final Composite parent
) {
1322 final Composite specsPanel
= new Composite(parent
, SWT
.NONE
);
1323 specsPanel
.setLayoutData(new GridData(SWT
.RIGHT
, SWT
.CENTER
, true,
1325 final RowLayout layout
= new RowLayout();
1326 layout
.spacing
= 10;
1327 specsPanel
.setLayout(layout
);
1329 forceUpdateAllButton
= new Button(specsPanel
, SWT
.PUSH
);
1330 forceUpdateAllButton
.setText(UIText
.RefSpecPanel_forceAll
);
1331 forceUpdateAllButton
.addSelectionListener(new SelectionAdapter() {
1333 public void widgetSelected(SelectionEvent e
) {
1334 final List
<RefSpec
> specsCopy
= new ArrayList
<RefSpec
>(specs
);
1335 for (final RefSpec spec
: specsCopy
) {
1336 if (!isDeleteRefSpec(spec
))
1337 setRefSpec(spec
, spec
.setForceUpdate(true));
1341 forceUpdateAllButton
1342 .setToolTipText(UIText
.RefSpecPanel_forceAllDescription
);
1343 updateForceUpdateAllButton();
1345 removeAllSpecButton
= new Button(specsPanel
, SWT
.PUSH
);
1346 removeAllSpecButton
.setImage(imageRegistry
.get(IMAGE_CLEAR
));
1347 removeAllSpecButton
.setText(UIText
.RefSpecPanel_removeAll
);
1348 removeAllSpecButton
.addSelectionListener(new SelectionAdapter() {
1350 public void widgetSelected(SelectionEvent e
) {
1355 .setToolTipText(UIText
.RefSpecPanel_removeAllDescription
);
1356 updateRemoveAllSpecButton();
1358 addRefSpecTableListener(new SelectionChangeListener() {
1359 public void selectionChanged() {
1360 updateForceUpdateAllButton();
1361 updateRemoveAllSpecButton();
1366 private void tryAutoCompleteSrcToDst() {
1367 final String src
= creationSrcCombo
.getText();
1368 final String dst
= creationDstCombo
.getText();
1370 if (src
== null || src
.length() == 0)
1373 if (dst
!= null && dst
.length() > 0) {
1374 // dst is already there, just fix wildcards if needed
1375 final String newDst
;
1376 if (RefSpec
.isWildcard(src
))
1377 newDst
= wildcardSpecComponent(dst
);
1379 newDst
= unwildcardSpecComponent(dst
, src
);
1380 creationDstCombo
.setText(newDst
);
1384 if (!isValidRefExpression(src
)) {
1385 // no way to be smarter than user here
1389 // dst is empty, src is ref or wildcard, so we can rewrite it as user
1392 creationDstCombo
.setText(src
);
1394 for (final RefSpec spec
: predefinedConfigured
) {
1395 if (spec
.matchSource(src
)) {
1396 final String newDst
= spec
.expandFromSource(src
)
1398 creationDstCombo
.setText(newDst
);
1402 if (remoteName
!= null && src
.startsWith(Constants
.R_HEADS
)) {
1403 final String newDst
= Constants
.R_REMOTES
+ remoteName
+ '/'
1404 + src
.substring(Constants
.R_HEADS
.length());
1405 creationDstCombo
.setText(newDst
);
1410 private void tryAutoCompleteDstToSrc() {
1411 final String src
= creationSrcCombo
.getText();
1412 final String dst
= creationDstCombo
.getText();
1414 if (dst
== null || dst
.length() == 0)
1417 if (src
!= null && src
.length() > 0) {
1418 // src is already there, fix wildcards if needed
1419 final String newSrc
;
1420 if (RefSpec
.isWildcard(dst
))
1421 newSrc
= wildcardSpecComponent(src
);
1423 newSrc
= unwildcardSpecComponent(src
, dst
);
1424 creationSrcCombo
.setText(newSrc
);
1429 private void validateCreationPanel() {
1430 final String src
= creationSrcCombo
.getText();
1431 final String dst
= creationDstCombo
.getText();
1433 // check src ref field
1434 boolean srcOk
= false;
1435 final boolean srcWildcard
= RefSpec
.isWildcard(src
);
1436 if (src
== null || src
.length() == 0)
1437 setControlDecoration(creationSrcDecoration
,
1438 FieldDecorationRegistry
.DEC_REQUIRED
,
1439 UIText
.RefSpecPanel_validationSrcUpdateRequired
);
1440 else if (pushSpecs
) {
1441 if (!srcWildcard
&& !isLocalRef(src
))
1442 setControlDecoration(creationSrcDecoration
,
1443 FieldDecorationRegistry
.DEC_ERROR
, NLS
.bind(
1444 UIText
.RefSpecPanel_validationRefInvalidLocal
,
1446 else if (srcWildcard
&& !isValidRefExpression(src
))
1447 setControlDecoration(
1448 creationSrcDecoration
,
1449 FieldDecorationRegistry
.DEC_ERROR
,
1452 UIText
.RefSpecPanel_validationRefInvalidExpression
,
1456 if (srcWildcard
&& !isMatchingAny(src
, localRefNames
))
1457 setControlDecoration(
1458 creationSrcDecoration
,
1459 FieldDecorationRegistry
.DEC_WARNING
,
1462 UIText
.RefSpecPanel_validationRefNonMatchingLocal
,
1465 creationSrcDecoration
.hide();
1468 if (!srcWildcard
&& !isRemoteRef(src
))
1469 setControlDecoration(
1470 creationSrcDecoration
,
1471 FieldDecorationRegistry
.DEC_ERROR
,
1474 UIText
.RefSpecPanel_validationRefNonExistingRemote
,
1476 else if (srcWildcard
&& !isMatchingAny(src
, remoteRefNames
)) {
1477 setControlDecoration(
1478 creationSrcDecoration
,
1479 FieldDecorationRegistry
.DEC_WARNING
,
1482 UIText
.RefSpecPanel_validationRefNonMatchingRemote
,
1487 creationSrcDecoration
.hide();
1491 // check dst ref field
1492 boolean dstOk
= false;
1493 if (dst
== null || dst
.length() == 0)
1494 setControlDecoration(creationDstDecoration
,
1495 FieldDecorationRegistry
.DEC_REQUIRED
,
1496 UIText
.RefSpecPanel_validationDstRequired
);
1497 else if (!isValidRefExpression(dst
))
1498 setControlDecoration(creationDstDecoration
,
1499 FieldDecorationRegistry
.DEC_ERROR
, NLS
.bind(
1500 UIText
.RefSpecPanel_validationDstInvalidExpression
,
1503 creationDstDecoration
.hide();
1506 // leave duplicates dst checking for validateSpecs()
1508 // check the wildcard synergy
1509 boolean wildcardOk
= true;
1510 if (srcOk
&& dstOk
&& (srcWildcard ^ RefSpec
.isWildcard(dst
))) {
1511 setControlDecoration(creationSrcDecoration
,
1512 FieldDecorationRegistry
.DEC_ERROR
,
1513 UIText
.RefSpecPanel_validationWildcardInconsistent
);
1514 setControlDecoration(creationDstDecoration
,
1515 FieldDecorationRegistry
.DEC_ERROR
,
1516 UIText
.RefSpecPanel_validationWildcardInconsistent
);
1520 creationButton
.setEnabled(srcOk
&& dstOk
&& wildcardOk
);
1523 private void validateDeleteCreationPanel() {
1524 final String ref
= deleteRefCombo
.getText();
1526 deleteButton
.setEnabled(false);
1527 if (ref
== null || ref
.length() == 0)
1528 setControlDecoration(deleteRefDecoration
,
1529 FieldDecorationRegistry
.DEC_REQUIRED
,
1530 UIText
.RefSpecPanel_validationRefDeleteRequired
);
1531 else if (!isValidRefExpression(ref
))
1532 setControlDecoration(deleteRefDecoration
,
1533 FieldDecorationRegistry
.DEC_ERROR
, NLS
.bind(
1534 UIText
.RefSpecPanel_validationRefInvalidExpression
,
1536 else if (RefSpec
.isWildcard(ref
))
1537 setControlDecoration(deleteRefDecoration
,
1538 FieldDecorationRegistry
.DEC_ERROR
,
1539 UIText
.RefSpecPanel_validationRefDeleteWildcard
);
1540 else if (!isRemoteRef(ref
))
1541 setControlDecoration(
1542 deleteRefDecoration
,
1543 FieldDecorationRegistry
.DEC_ERROR
,
1546 UIText
.RefSpecPanel_validationRefNonExistingRemoteDelete
,
1549 deleteRefDecoration
.hide();
1550 deleteButton
.setEnabled(true);
1554 private void validateSpecs() {
1555 // validate spec; display max. 1 error message for user at time
1556 final RefSpec oldInvalidSpec
= invalidSpec
;
1557 final RefSpec oldInvalidSpecSameDst
= invalidSpecSameDst
;
1558 errorMessage
= null;
1560 invalidSpecSameDst
= null;
1561 for (final RefSpec spec
: specs
) {
1562 errorMessage
= validateSpec(spec
);
1563 if (errorMessage
!= null) {
1568 if (errorMessage
== null)
1569 validateSpecsCrossDst();
1570 if (invalidSpec
!= oldInvalidSpec
1571 || invalidSpecSameDst
!= oldInvalidSpecSameDst
)
1572 tableViewer
.refresh();
1575 private String
validateSpec(final RefSpec spec
) {
1576 final String src
= spec
.getSource();
1577 final String dst
= spec
.getDestination();
1578 final boolean wildcard
= spec
.isWildcard();
1582 if (!isDeleteRefSpec(spec
)) {
1583 if (src
.length() == 0)
1584 return UIText
.RefSpecPanel_validationSrcUpdateRequired
;
1585 else if (!wildcard
&& !isLocalRef(src
))
1587 UIText
.RefSpecPanel_validationRefInvalidLocal
, src
);
1588 else if (wildcard
&& !isValidRefExpression(src
))
1590 UIText
.RefSpecPanel_validationRefInvalidExpression
,
1592 // ignore non-matching wildcard specs
1595 if (src
== null || src
.length() == 0)
1596 return UIText
.RefSpecPanel_validationSrcUpdateRequired
;
1597 else if (!wildcard
&& !isRemoteRef(src
))
1600 UIText
.RefSpecPanel_validationRefNonExistingRemote
,
1602 // ignore non-matching wildcard specs
1606 if (dst
== null || dst
.length() == 0) {
1607 if (isDeleteRefSpec(spec
))
1608 return UIText
.RefSpecPanel_validationRefDeleteRequired
;
1609 return UIText
.RefSpecPanel_validationDstRequired
;
1610 } else if (!isValidRefExpression(dst
))
1611 return NLS
.bind(UIText
.RefSpecPanel_validationRefInvalidExpression
,
1613 else if (isDeleteRefSpec(spec
) && !isRemoteRef(dst
))
1615 UIText
.RefSpecPanel_validationRefNonExistingRemoteDelete
,
1621 private boolean isInvalidSpec(Object element
) {
1622 return element
== invalidSpec
|| element
== invalidSpecSameDst
;
1625 private void validateSpecsCrossDst() {
1626 final Map
<String
, RefSpec
> dstsSpecsMap
= new HashMap
<String
, RefSpec
>();
1628 for (final RefSpec spec
: specs
) {
1629 if (!spec
.isWildcard()) {
1630 if (!tryAddDestination(dstsSpecsMap
, spec
.getDestination(),
1634 final Collection
<String
> srcNames
;
1636 srcNames
= localRefNames
;
1638 srcNames
= remoteRefNames
;
1640 for (final String src
: srcNames
) {
1641 if (spec
.matchSource(src
)) {
1642 final String dst
= spec
.expandFromSource(src
)
1644 if (!tryAddDestination(dstsSpecsMap
, dst
, spec
))
1651 matchingAnyRefs
= !dstsSpecsMap
.isEmpty();
1655 private boolean tryAddDestination(final Map
<String
, RefSpec
> dstsSpecsMap
,
1656 final String dst
, final RefSpec spec
) {
1657 final RefSpec other
= dstsSpecsMap
.put(dst
, spec
);
1658 if (other
!= null) {
1661 UIText
.RefSpecPanel_validationSpecificationsOverlappingDestination
,
1663 invalidSpec
= other
;
1664 invalidSpecSameDst
= spec
;
1670 private void updateAddPredefinedButton(final Button button
,
1671 final List
<RefSpec
> predefined
) {
1672 boolean enable
= false;
1673 for (final RefSpec pre
: predefined
) {
1674 if (!specs
.contains(pre
)) {
1679 button
.setEnabled(enable
);
1682 private void updateAddPredefinedButton(Button button
,
1683 final RefSpec predefined
) {
1684 button
.setEnabled(!specs
.contains(predefined
));
1687 private void updateForceUpdateAllButton() {
1688 boolean enable
= false;
1689 for (final RefSpec spec
: specs
) {
1690 if (!isDeleteRefSpec(spec
) && !spec
.isForceUpdate()) {
1695 forceUpdateAllButton
.setEnabled(enable
);
1698 private void updateRemoveAllSpecButton() {
1699 removeAllSpecButton
.setEnabled(!specs
.isEmpty());
1702 private String
typeString() {
1703 return (pushSpecs ? UIText
.RefSpecPanel_push
1704 : UIText
.RefSpecPanel_fetch
);
1707 private void addPredefinedRefSpecs(final RefSpec predefined
) {
1708 addPredefinedRefSpecs(Collections
.singletonList(predefined
));
1711 private void addPredefinedRefSpecs(final List
<RefSpec
> predefined
) {
1712 for (final RefSpec pre
: predefined
) {
1713 if (!specs
.contains(pre
))
1718 private List
<RefContentProposal
> createContentProposals(
1719 final Collection
<Ref
> refs
, final Ref HEAD
) {
1720 final TreeSet
<Ref
> set
= new TreeSet
<Ref
>(new Comparator
<Ref
>() {
1721 public int compare(Ref o1
, Ref o2
) {
1722 // lexicographical ordering by name seems to be fine
1723 return o1
.getName().compareTo(o2
.getName());
1730 final List
<RefContentProposal
> result
= new ArrayList
<RefContentProposal
>(
1732 for (final Ref r
: set
)
1733 result
.add(new RefContentProposal(localDb
, r
));
1737 private List
<RefContentProposal
> createProposalsFilteredLocal(
1738 final List
<RefContentProposal
> proposals
) {
1739 final List
<RefContentProposal
> result
= new ArrayList
<RefContentProposal
>();
1740 for (final RefContentProposal p
: proposals
) {
1741 final String content
= p
.getContent();
1743 if (content
.equals(Constants
.HEAD
)
1744 || content
.startsWith(Constants
.R_HEADS
))
1747 if (content
.startsWith(Constants
.R_REMOTES
))
1754 private boolean isRemoteRef(String ref
) {
1755 return remoteRefNames
.contains(ref
);
1758 private boolean isLocalRef(final String ref
) {
1759 return tryResolveLocalRef(ref
) != null;
1762 private boolean isMatchingAny(final String ref
,
1763 final Collection
<String
> names
) {
1764 // strip wildcard sign
1765 final String prefix
= ref
.substring(0, ref
.length() - 1);
1766 for (final String name
: names
)
1767 if (name
.startsWith(prefix
))
1772 private ObjectId
tryResolveLocalRef(final String ref
) {
1774 return localDb
.resolve(ref
);
1775 } catch (final IOException e
) {
1777 "I/O error occurred during resolving expression: " //$NON-NLS-1$
1783 private class RefContentProposalProvider
implements
1784 IContentProposalProvider
{
1785 private List
<RefContentProposal
> proposals
= Collections
.emptyList();
1787 private final boolean tryResolvingLocally
;
1789 private RefContentProposalProvider(final boolean tryResolvingLocally
) {
1790 this.tryResolvingLocally
= tryResolvingLocally
;
1793 private void setProposals(final List
<RefContentProposal
> proposals
) {
1794 this.proposals
= proposals
;
1797 public IContentProposal
[] getProposals(final String contents
,
1799 final List
<RefContentProposal
> result
= new ArrayList
<RefContentProposal
>();
1801 if (contents
.indexOf('*') != -1 || contents
.indexOf('?') != -1) {
1802 // contents contains wildcards
1804 // check if contents can be safely added as wildcard spec
1805 if (isValidRefExpression(contents
))
1806 result
.add(new RefContentProposal(localDb
, contents
, null));
1808 // let's expand wildcards
1809 final String regex
= ".*" //$NON-NLS-1$
1810 + contents
.replace("*", ".*").replace("?", ".?") + ".*"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
1811 final Pattern p
= Pattern
.compile(regex
);
1812 for (final RefContentProposal prop
: proposals
)
1813 if (p
.matcher(prop
.getContent()).matches())
1816 for (final RefContentProposal prop
: proposals
)
1817 if (prop
.getContent().contains(contents
))
1820 if (tryResolvingLocally
&& result
.isEmpty()) {
1821 final ObjectId id
= tryResolveLocalRef(contents
);
1824 .add(new RefContentProposal(localDb
, contents
,
1828 return result
.toArray(new IContentProposal
[0]);