/******************************************************************************* * Copyright (C) 2010, Dariusz Luksza <dariusz@luksza.org> * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.eclipse.egit.ui.internal.dialogs; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import org.eclipse.core.runtime.Assert; import org.eclipse.egit.ui.UIUtils; import org.eclipse.egit.ui.internal.UIText; import org.eclipse.jface.fieldassist.ComboContentAdapter; import org.eclipse.jface.fieldassist.ContentProposalAdapter; import org.eclipse.jface.fieldassist.IContentProposal; import org.eclipse.jface.fieldassist.IContentProposalProvider; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; /** * This is an extended version of {@link Combo} widget with is specialized in * displaying commits and simplifying selection process. * * It is integrated with {@link ContentProposalAdapter} that helps select * preferred tag by user. To get activate proposal provider simply just start * writing commit SHA-1 or part of commit's message first line */ public class CommitCombo extends Composite { private final List<ComboCommitEnt> commits; private final Combo combo; private static class ComboCommitEnt { private final String message; private final ObjectId objectId; public ComboCommitEnt(ObjectId objecId, String message) { this.objectId = objecId; this.message = message; } } private class CommitContentProposalProvider implements IContentProposalProvider { @Override public IContentProposal[] getProposals(String contents, int position) { List<IContentProposal> list = new ArrayList<>(); Pattern pattern = Pattern.compile(contents, Pattern.CASE_INSENSITIVE); for (int i = 0; i < commits.size(); i++) { String message = commits.get(i).message; if (message.length() >= contents.length() && pattern.matcher(message).find()) { list.add(makeContentProposal(message)); } } return list.toArray(new IContentProposal[] {}); } /* * Make an IContentProposal for showing the specified String. */ private IContentProposal makeContentProposal(final String proposal) { return new IContentProposal() { @Override public String getContent() { return proposal; } @Override public String getDescription() { return null; } @Override public String getLabel() { return null; } @Override public int getCursorPosition() { return proposal.length(); } }; } } /** * Constructs a new instance of this class given its parent and a style * value describing its behavior and appearance. * * @param parent * a widget which will be the parent of the new instance (cannot * be null) * @param style * the SWT style bits */ public CommitCombo(Composite parent, int style) { super(parent, style); combo = new Combo(this, SWT.DROP_DOWN); commits = new ArrayList<>(); setLayout(GridLayoutFactory.swtDefaults().create()); setLayoutData(GridDataFactory.fillDefaults().create()); GridData totalLabelData = new GridData(); totalLabelData.horizontalAlignment = SWT.FILL; totalLabelData.grabExcessHorizontalSpace = true; combo.setLayoutData(totalLabelData); combo.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { if (null == getValue()) combo.setText(""); //$NON-NLS-1$ } }); UIUtils .addBulbDecorator(combo, UIText.CommitCombo_showSuggestedCommits); ContentProposalAdapter adapter = new ContentProposalAdapter(combo, new ComboContentAdapter(), new CommitContentProposalProvider(), null, null); adapter.setPropagateKeys(true); adapter .setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); } /** * Add a {@link RevCommit} to widget. * * @param revCommit */ public void add(RevCommit revCommit) { Assert.isNotNull(revCommit); checkWidget(); String shortSha1 = revCommit.abbreviate(8).name(); String message = shortSha1 + ": " + revCommit.getShortMessage(); //$NON-NLS-1$ combo.add(message); commits.add(new ComboCommitEnt(revCommit.getId(), message)); } /** * Returns value of SHA-1 for selected commit. * * @param index * index of item in check box * @return SHA-1 of selected commit */ public ObjectId getItem(int index) { checkWidget(); if (!(0 <= index && index < commits.size())) { SWT.error(SWT.ERROR_INVALID_RANGE); } return commits.get(index).objectId; } /** * @return the number of items */ public int getItemCount() { return commits.size(); } /** * @return index of selected element */ public int getSelectedIndex() { int selectionIndex = combo.getSelectionIndex(); if (selectionIndex == -1) selectionIndex = combo.indexOf(combo.getText()); return selectionIndex; } /** * @return SHA-1 of selected commit */ public ObjectId getValue() { int selectionIndex = getSelectedIndex(); return -1 != selectionIndex ? getItem(selectionIndex) : null; } /** * Selects the item with is associated with given <code>objectId</code> * * @param objectId */ public void setSelectedElement(ObjectId objectId) { if (objectId == null) { return; } for (int i = 0; i < commits.size(); i++) if (objectId.equals(commits.get(i).objectId)) { combo.select(i); break; } } @Override public void setEnabled(boolean enabled) { combo.setEnabled(enabled); super.setEnabled(enabled); } /** * Sets the selection in the receiver's text field to an empty selection * starting just before the first character. If the text field is editable, * this has the effect of placing the i-beam at the start of the text. */ public void clearSelection() { combo.clearSelection(); combo.setText(""); //$NON-NLS-1$ } }