rename org.spearce.egit -> org.eclipse.egit and bump version to 0.5.0
[egit/imyousuf.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / components / RefSpecPanel.java
blobc5d7a995c3ac36d12bcedfde711a63ef228cf684
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;
20 import java.util.Map;
21 import java.util.Set;
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;
88 /**
89 * This class provides universal panel for editing list of {@link RefSpec} -
90 * specifications for both push or fetch, depending on panel configuration.
91 * <p>
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.).
97 * <p>
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}.
101 * <p>
102 * Typical class usage:
104 * <pre>
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);
113 * // get result data
114 * List&lt;RefSpec&gt; result = panel.getRefSpecs();
115 * // further processing: push or save configuration...
116 * </pre>
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
150 return Repository
151 .isValidRefName(s.substring(0, s.length() - 1) + 'X');
152 } else
153 return Repository.isValidRefName(s);
156 private static RefSpec setRefSpecSource(final RefSpec spec, final String src) {
157 final String dst;
158 if (RefSpec.isWildcard(src))
159 dst = wildcardSpecComponent(spec.getDestination());
160 else
161 dst = unwildcardSpecComponent(spec.getDestination(), src);
162 return spec.setSourceDestination(src, dst);
165 private static RefSpec setRefSpecDestination(final RefSpec spec,
166 final String dst) {
167 final String src;
168 if (RefSpec.isWildcard(dst))
169 src = wildcardSpecComponent(spec.getSource());
170 else
171 src = unwildcardSpecComponent(spec.getSource(), dst);
172 return spec.setSourceDestination(src, dst);
175 private static String wildcardSpecComponent(final String comp) {
176 final int i;
177 if (RefSpec.isWildcard(comp))
178 return 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))
189 return 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))
203 result.add(p);
205 return result;
208 private static Image getDecorationImage(final String key) {
209 return FieldDecorationRegistry.getDefault().getFieldDecoration(key)
210 .getImage();
213 private static void setControlDecoration(final ControlDecoration control,
214 final String imageKey, final String description) {
215 control.setImage(getDecorationImage(imageKey));
216 control.setDescriptionText(description);
217 control.show();
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.
308 * <p>
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.
314 * @param parent
315 * parent control for panel.
316 * @param pushSpecs
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();
332 if (pushSpecs)
333 createDeleteCreationPanel();
334 createPredefinedCreationPanel();
335 createTableGroup();
337 addRefSpecTableListener(new SelectionChangeListener() {
338 public void selectionChanged() {
339 validateSpecs();
342 setEnable(false);
346 * Enable or disable panel controls.
348 * @param enable
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.
359 * @param localRepo
360 * local repository where specifications will be applied.
361 * @param remoteRefs
362 * collection of remote refs as advertised by remote repository.
363 * Typically they are collected by {@link FetchConnection}
364 * implementation.
365 * @param remoteName
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(
377 remoteRefs, null);
378 remoteProposalProvider.setProposals(remoteProposals);
379 remoteRefNames = new HashSet<String>();
380 for (final RefContentProposal p : remoteProposals)
381 remoteRefNames.add(p.getContent());
383 Ref HEAD = null;
384 try {
385 final ObjectId id = localDb.resolve(Constants.HEAD);
386 if (id != null)
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);
401 if (pushSpecs) {
402 creationSrcComboSupport.setProposals(localFilteredProposals);
403 creationDstComboSupport.setProposals(remoteFilteredProposals);
404 } else {
405 creationSrcComboSupport.setProposals(remoteFilteredProposals);
406 creationDstComboSupport.setProposals(localFilteredProposals);
408 validateCreationPanel();
410 if (pushSpecs) {
411 deleteRefComboSupport.setProposals(remoteFilteredProposals);
412 validateDeleteCreationPanel();
415 try {
416 if (remoteName == null)
417 predefinedConfigured = Collections.emptyList();
418 else {
419 final RemoteConfig rc = new RemoteConfig(localDb.getConfig(),
420 remoteName);
421 if (pushSpecs)
422 predefinedConfigured = rc.getPushRefSpecs();
423 else
424 predefinedConfigured = rc.getFetchRefSpecs();
425 for (final RefSpec spec : predefinedConfigured)
426 addRefSpec(spec);
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
434 .getMessage(), e));
436 updateAddPredefinedButton(addConfiguredButton, predefinedConfigured);
437 if (pushSpecs)
438 predefinedBranches = Transport.REFSPEC_PUSH_ALL;
439 else {
440 final String r;
441 if (remoteName == null)
442 r = UIText.RefSpecPanel_refChooseRemoteName;
443 else
444 r = remoteName;
445 predefinedBranches = new RefSpec("refs/heads/*:refs/remotes/" //$NON-NLS-1$
446 + r + "/*"); //$NON-NLS-1$
448 updateAddPredefinedButton(addBranchesButton, predefinedBranches);
449 setEnable(true);
453 * @return underlying control for this panel.
455 public Control getControl() {
456 return panel;
460 * Return current list of specifications of this panel.
461 * <p>
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.
492 * <p>
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.
496 * <p>
497 * This method should be called only from the UI thread.
499 * @param spec
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);
506 if (i != -1)
507 throw new IllegalArgumentException("RefSpec " + spec //$NON-NLS-1$
508 + " already exists."); //$NON-NLS-1$
509 specs.add(spec);
510 tableViewer.add(spec);
511 notifySpecsChanged();
515 * Remove provided specification from this panel. Panel view is
516 * automatically refreshed, model is revalidated.
517 * <p>
518 * Provided specification must be equals with existing one in terms of
519 * reference equality, not an equals method.
520 * <p>
521 * This method should be called only from the UI thread.
523 * @param spec
524 * specification to remove.
525 * @throws IllegalArgumentException
526 * if specification with this reference doesn't exist in this
527 * panel.
529 public void removeRefSpec(final RefSpec spec) {
530 final int i = indexOfSpec(spec);
531 if (i == -1)
532 throw new IllegalArgumentException("RefSpec " + spec //$NON-NLS-1$
533 + " not found."); //$NON-NLS-1$
534 specs.remove(i);
535 tableViewer.remove(spec);
536 notifySpecsChanged();
540 * Change some specification to the new one.
541 * <p>
542 * Old specification must exist in the panel, while new specification can't
543 * exist before (both in terms of reference equality).
544 * <p>
545 * This method should be called only from the UI thread.
547 * @param oldSpec
548 * specification to change. Can't be null.
549 * @param newSpec
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);
554 if (oldI == -1)
555 throw new IllegalArgumentException("RefSpec " + oldSpec //$NON-NLS-1$
556 + " not found."); //$NON-NLS-1$
557 final int newI = indexOfSpec(newSpec);
558 if (newI != -1)
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.
571 * <p>
572 * This method should be called only from the UI thread.
574 public void clearRefSpecs() {
575 final RefSpec toRemove[] = specs.toArray(new RefSpec[0]);
576 specs.clear();
577 tableViewer.remove(toRemove);
578 notifySpecsChanged();
582 * Add listener of changes in panel model.
583 * <p>
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.
587 * <p>
588 * Order of adding listeners is significant. This method is not thread-safe.
589 * Listeners should be set up before panel usage.
591 * @param listener
592 * listener to add.
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() {
604 return errorMessage;
608 * Return information about validity of specifications.
609 * <p>
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) {
625 int i;
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)
630 break;
632 if (i == specs.size())
633 return -1;
634 return i;
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,
668 false));
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,
681 false, 1, 2));
682 creationButton.setImage(imageRegistry.get(IMAGE_ADD));
683 creationButton.setText(UIText.RefSpecPanel_creationButton);
684 creationButton.addSelectionListener(new SelectionAdapter() {
685 @Override
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);
690 addRefSpec(spec);
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,
707 false));
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();
716 if (pushSpecs)
717 creationSrcCombo
718 .setToolTipText(UIText.RefSpecPanel_srcPushDescription);
719 else
720 creationSrcCombo
721 .setToolTipText(UIText.RefSpecPanel_srcFetchDescription);
722 creationSrcComboSupport = new ComboLabelingSupport(creationSrcCombo,
723 new SelectionAdapter() {
724 @Override
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,
739 false));
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();
748 if (pushSpecs)
749 creationDstCombo
750 .setToolTipText(UIText.RefSpecPanel_dstPushDescription);
751 else
752 creationDstCombo
753 .setToolTipText(UIText.RefSpecPanel_dstFetchDescription);
754 creationDstComboSupport = new ComboLabelingSupport(creationDstCombo,
755 new SelectionAdapter() {
756 @Override
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);
775 deletePanel
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,
790 false));
791 deleteRefCombo
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,
797 false));
798 deleteButton.setImage(imageRegistry.get(IMAGE_DELETE));
799 deleteButton.setText(UIText.RefSpecPanel_deletionButton);
800 deleteButton.addSelectionListener(new SelectionAdapter() {
801 @Override
802 public void widgetSelected(SelectionEvent e) {
803 RefSpec spec = new RefSpec(':' + deleteRefCombo.getText());
804 addRefSpec(spec);
805 deleteRefCombo.setText(""); //$NON-NLS-1$
808 deleteButton
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,
822 false));
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,
830 true, false));
831 addConfiguredButton.setText(NLS.bind(
832 UIText.RefSpecPanel_predefinedConfigured, typeString()));
833 addConfiguredButton.addSelectionListener(new SelectionAdapter() {
834 @Override
835 public void widgetSelected(SelectionEvent e) {
836 addPredefinedRefSpecs(predefinedConfigured);
839 addConfiguredButton
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,
845 true, false));
846 addBranchesButton.setText(UIText.RefSpecPanel_predefinedAll);
847 addBranchesButton.addSelectionListener(new SelectionAdapter() {
848 @Override
849 public void widgetSelected(SelectionEvent e) {
850 addPredefinedRefSpecs(predefinedBranches);
853 addBranchesButton
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,
859 false));
860 addTagsButton.setText(UIText.RefSpecPanel_predefinedTags);
861 addTagsButton.addSelectionListener(new SelectionAdapter() {
862 @Override
863 public void widgetSelected(SelectionEvent e) {
864 addPredefinedRefSpecs(Transport.REFSPEC_TAGS);
867 addTagsButton
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
888 // 3.4)
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,
894 true);
895 proposal
896 .setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
897 if (listener != null)
898 proposal.addContentProposalListener(listener);
899 return decoration;
902 private void createTableGroup() {
903 final Group tableGroup = new Group(panel, SWT.NONE);
904 tableGroup.setText(NLS.bind(UIText.RefSpecPanel_specifications,
905 typeString()));
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,
940 Object newInput) {
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
950 return a == b;
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);
964 if (pushSpecs)
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() {
985 @Override
986 public String getText(final Object element) {
987 return (isDeleteRefSpec(element) ? UIText.RefSpecPanel_modeDelete
988 : UIText.RefSpecPanel_modeUpdate);
991 @Override
992 public Image getImage(Object element) {
993 return (isDeleteRefSpec(element) ? imageRegistry
994 .get(IMAGE_DELETE) : imageRegistry.get(IMAGE_ADD));
997 @Override
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) {
1007 @Override
1008 protected boolean canEdit(final Object element) {
1009 return true;
1012 @Override
1013 protected CellEditor getCellEditor(final Object element) {
1014 return modeCellEditor;
1017 @Override
1018 protected Object getValue(final Object element) {
1019 return isDeleteRefSpec(element);
1022 @Override
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);
1029 } else {
1030 newSpec = setRefSpecSource(oldSpec,
1031 UIText.RefSpecPanel_refChooseSome);
1032 setRefSpec(oldSpec, newSpec);
1033 tableViewer.getControl().getDisplay().asyncExec(
1034 new Runnable() {
1035 public void run() {
1036 tableViewer.editElement(newSpec,
1037 srcColumnIndex);
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() {
1049 @Override
1050 public String getText(final Object element) {
1051 return ((RefSpec) element).getSource();
1054 @Override
1055 public String getToolTipText(Object element) {
1056 if (isInvalidSpec(element))
1057 return errorMessage;
1058 if (isDeleteRefSpec(element))
1059 return UIText.RefSpecPanel_srcDeleteDescription;
1060 if (pushSpecs)
1061 return UIText.RefSpecPanel_srcPushDescription;
1062 return UIText.RefSpecPanel_srcFetchDescription;
1065 @Override
1066 public Color getBackground(final Object element) {
1067 if (isInvalidSpec(element))
1068 return errorBackgroundColor;
1069 return null;
1072 @Override
1073 public Color getToolTipForegroundColor(Object element) {
1074 if (isInvalidSpec(element))
1075 return errorTextColor;
1076 return null;
1079 column.setEditingSupport(new EditingSupport(tableViewer) {
1080 @Override
1081 protected boolean canEdit(final Object element) {
1082 return !isDeleteRefSpec(element);
1085 @Override
1086 protected CellEditor getCellEditor(final Object element) {
1087 return (pushSpecs ? localRefCellEditor : remoteRefCellEditor);
1090 @Override
1091 protected Object getValue(final Object element) {
1092 return ((RefSpec) element).getSource();
1095 @Override
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.
1103 return;
1106 final RefSpec oldSpec = (RefSpec) element;
1107 final RefSpec newSpec = setRefSpecSource(oldSpec,
1108 (String) value);
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())
1117 break;
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() {
1125 @Override
1126 public String getText(final Object element) {
1127 return ((RefSpec) element).getDestination();
1130 @Override
1131 public String getToolTipText(Object element) {
1132 if (isInvalidSpec(element))
1133 return errorMessage;
1134 if (isDeleteRefSpec(element))
1135 return UIText.RefSpecPanel_dstDeletionDescription;
1136 if (pushSpecs)
1137 return UIText.RefSpecPanel_dstPushDescription;
1138 return UIText.RefSpecPanel_dstFetchDescription;
1141 @Override
1142 public Color getBackground(final Object element) {
1143 if (isInvalidSpec(element))
1144 return errorBackgroundColor;
1145 return null;
1148 @Override
1149 public Color getToolTipForegroundColor(Object element) {
1150 if (isInvalidSpec(element))
1151 return errorTextColor;
1152 return null;
1155 column.setEditingSupport(new EditingSupport(tableViewer) {
1156 @Override
1157 protected boolean canEdit(final Object element) {
1158 return true;
1161 @Override
1162 protected CellEditor getCellEditor(final Object element) {
1163 return (pushSpecs ? remoteRefCellEditor : localRefCellEditor);
1166 @Override
1167 protected Object getValue(final Object element) {
1168 return ((RefSpec) element).getDestination();
1171 @Override
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.
1177 return;
1180 final RefSpec oldSpec = (RefSpec) element;
1181 final RefSpec newSpec = setRefSpecDestination(oldSpec,
1182 (String) value);
1183 setRefSpec(oldSpec, newSpec);
1188 private void createForceColumn(final TableColumnLayout columnLayout) {
1189 final TableViewerColumn column = createColumn(columnLayout,
1190 UIText.RefSpecPanel_columnForce, COLUMN_FORCE_WEIGHT,
1191 SWT.CENTER);
1192 column.setLabelProvider(new CheckboxLabelProvider(tableViewer
1193 .getControl()) {
1194 @Override
1195 protected boolean isChecked(final Object element) {
1196 return ((RefSpec) element).isForceUpdate();
1199 @Override
1200 protected boolean isEnabled(Object element) {
1201 return !isDeleteRefSpec(element);
1204 @Override
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) {
1216 @Override
1217 protected boolean canEdit(final Object element) {
1218 return !isDeleteRefSpec(element);
1221 @Override
1222 protected CellEditor getCellEditor(final Object element) {
1223 return forceUpdateCellEditor;
1226 @Override
1227 protected Object getValue(final Object element) {
1228 return ((RefSpec) element).isForceUpdate();
1231 @Override
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,
1243 SWT.CENTER);
1244 column.setLabelProvider(new CenteredImageLabelProvider() {
1245 @Override
1246 public Image getImage(Object element) {
1247 return imageRegistry.get(IMAGE_TRASH);
1250 @Override
1251 public String getToolTipText(Object element) {
1252 return NLS.bind(UIText.RefSpecPanel_removeDescription,
1253 typeString());
1256 column.setEditingSupport(new EditingSupport(tableViewer) {
1257 @Override
1258 protected boolean canEdit(Object element) {
1259 return true;
1262 @Override
1263 protected CellEditor getCellEditor(Object element) {
1264 return removeSpecCellEditor;
1267 @Override
1268 protected Object getValue(Object element) {
1269 return null;
1272 @Override
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) {
1291 if (pushSpecs)
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,
1314 true);
1315 assist
1316 .setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
1318 return cellEditor;
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,
1324 false));
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() {
1332 @Override
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() {
1349 @Override
1350 public void widgetSelected(SelectionEvent e) {
1351 clearRefSpecs();
1354 removeAllSpecButton
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)
1371 return;
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);
1378 else
1379 newDst = unwildcardSpecComponent(dst, src);
1380 creationDstCombo.setText(newDst);
1381 return;
1384 if (!isValidRefExpression(src)) {
1385 // no way to be smarter than user here
1386 return;
1389 // dst is empty, src is ref or wildcard, so we can rewrite it as user
1390 // would perhaps
1391 if (pushSpecs)
1392 creationDstCombo.setText(src);
1393 else {
1394 for (final RefSpec spec : predefinedConfigured) {
1395 if (spec.matchSource(src)) {
1396 final String newDst = spec.expandFromSource(src)
1397 .getDestination();
1398 creationDstCombo.setText(newDst);
1399 return;
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)
1415 return;
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);
1422 else
1423 newSrc = unwildcardSpecComponent(src, dst);
1424 creationSrcCombo.setText(newSrc);
1425 return;
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,
1445 src));
1446 else if (srcWildcard && !isValidRefExpression(src))
1447 setControlDecoration(
1448 creationSrcDecoration,
1449 FieldDecorationRegistry.DEC_ERROR,
1451 .bind(
1452 UIText.RefSpecPanel_validationRefInvalidExpression,
1453 src));
1454 else {
1455 srcOk = true;
1456 if (srcWildcard && !isMatchingAny(src, localRefNames))
1457 setControlDecoration(
1458 creationSrcDecoration,
1459 FieldDecorationRegistry.DEC_WARNING,
1461 .bind(
1462 UIText.RefSpecPanel_validationRefNonMatchingLocal,
1463 src));
1464 else
1465 creationSrcDecoration.hide();
1467 } else {
1468 if (!srcWildcard && !isRemoteRef(src))
1469 setControlDecoration(
1470 creationSrcDecoration,
1471 FieldDecorationRegistry.DEC_ERROR,
1473 .bind(
1474 UIText.RefSpecPanel_validationRefNonExistingRemote,
1475 src));
1476 else if (srcWildcard && !isMatchingAny(src, remoteRefNames)) {
1477 setControlDecoration(
1478 creationSrcDecoration,
1479 FieldDecorationRegistry.DEC_WARNING,
1481 .bind(
1482 UIText.RefSpecPanel_validationRefNonMatchingRemote,
1483 src));
1484 srcOk = true;
1485 } else {
1486 srcOk = true;
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,
1501 dst));
1502 else {
1503 creationDstDecoration.hide();
1504 dstOk = true;
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);
1517 wildcardOk = false;
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,
1535 ref));
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,
1545 .bind(
1546 UIText.RefSpecPanel_validationRefNonExistingRemoteDelete,
1547 ref));
1548 else {
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;
1559 invalidSpec = null;
1560 invalidSpecSameDst = null;
1561 for (final RefSpec spec : specs) {
1562 errorMessage = validateSpec(spec);
1563 if (errorMessage != null) {
1564 invalidSpec = spec;
1565 break;
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();
1580 // check src
1581 if (pushSpecs) {
1582 if (!isDeleteRefSpec(spec)) {
1583 if (src.length() == 0)
1584 return UIText.RefSpecPanel_validationSrcUpdateRequired;
1585 else if (!wildcard && !isLocalRef(src))
1586 return NLS.bind(
1587 UIText.RefSpecPanel_validationRefInvalidLocal, src);
1588 else if (wildcard && !isValidRefExpression(src))
1589 return NLS.bind(
1590 UIText.RefSpecPanel_validationRefInvalidExpression,
1591 src);
1592 // ignore non-matching wildcard specs
1594 } else {
1595 if (src == null || src.length() == 0)
1596 return UIText.RefSpecPanel_validationSrcUpdateRequired;
1597 else if (!wildcard && !isRemoteRef(src))
1598 return NLS
1599 .bind(
1600 UIText.RefSpecPanel_validationRefNonExistingRemote,
1601 src);
1602 // ignore non-matching wildcard specs
1605 // check dst
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,
1612 dst);
1613 else if (isDeleteRefSpec(spec) && !isRemoteRef(dst))
1614 return NLS.bind(
1615 UIText.RefSpecPanel_validationRefNonExistingRemoteDelete,
1616 dst);
1618 return null;
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>();
1627 try {
1628 for (final RefSpec spec : specs) {
1629 if (!spec.isWildcard()) {
1630 if (!tryAddDestination(dstsSpecsMap, spec.getDestination(),
1631 spec))
1632 return;
1633 } else {
1634 final Collection<String> srcNames;
1635 if (pushSpecs)
1636 srcNames = localRefNames;
1637 else
1638 srcNames = remoteRefNames;
1640 for (final String src : srcNames) {
1641 if (spec.matchSource(src)) {
1642 final String dst = spec.expandFromSource(src)
1643 .getDestination();
1644 if (!tryAddDestination(dstsSpecsMap, dst, spec))
1645 return;
1650 } finally {
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) {
1659 errorMessage = NLS
1660 .bind(
1661 UIText.RefSpecPanel_validationSpecificationsOverlappingDestination,
1662 dst);
1663 invalidSpec = other;
1664 invalidSpecSameDst = spec;
1665 return false;
1667 return true;
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)) {
1675 enable = true;
1676 break;
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()) {
1691 enable = true;
1692 break;
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))
1714 addRefSpec(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());
1726 set.addAll(refs);
1727 if (HEAD != null)
1728 set.add(HEAD);
1730 final List<RefContentProposal> result = new ArrayList<RefContentProposal>(
1731 set.size());
1732 for (final Ref r : set)
1733 result.add(new RefContentProposal(localDb, r));
1734 return result;
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();
1742 if (pushSpecs) {
1743 if (content.equals(Constants.HEAD)
1744 || content.startsWith(Constants.R_HEADS))
1745 result.add(p);
1746 } else {
1747 if (content.startsWith(Constants.R_REMOTES))
1748 result.add(p);
1751 return result;
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))
1768 return true;
1769 return false;
1772 private ObjectId tryResolveLocalRef(final String ref) {
1773 try {
1774 return localDb.resolve(ref);
1775 } catch (final IOException e) {
1776 Activator.logError(
1777 "I/O error occurred during resolving expression: " //$NON-NLS-1$
1778 + ref, e);
1779 return null;
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,
1798 int position) {
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())
1814 result.add(prop);
1815 } else {
1816 for (final RefContentProposal prop : proposals)
1817 if (prop.getContent().contains(contents))
1818 result.add(prop);
1820 if (tryResolvingLocally && result.isEmpty()) {
1821 final ObjectId id = tryResolveLocalRef(contents);
1822 if (id != null)
1823 result
1824 .add(new RefContentProposal(localDb, contents,
1825 id));
1828 return result.toArray(new IContentProposal[0]);