Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchris.poon2015-08-31 20:23:54 -0400
committerGerrit Code Review @ Eclipse.org2015-09-02 18:16:54 -0400
commit304393bd6fde6b648d93870a825377a3b7eb6872 (patch)
tree91b1632c7c38edf4e02bcd7e716dd0050c45d8e0
parent9f4de2dba03de7b00c4db82f7e14ede218697447 (diff)
downloadorg.eclipse.mylyn.tasks-304393bd6fde6b648d93870a825377a3b7eb6872.tar.gz
org.eclipse.mylyn.tasks-304393bd6fde6b648d93870a825377a3b7eb6872.tar.xz
org.eclipse.mylyn.tasks-304393bd6fde6b648d93870a825377a3b7eb6872.zip
476383: add an attribute editor for label fields
Add a text attribute editor that supports single and multi values and has content assist Change-Id: I9e53666e713abb1affbb7b6376c5af7192174ddb Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=476383 Signed-off-by: chris.poon <chris.poon@tasktop.com>
-rw-r--r--org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/AllTasksTests.java1
-rw-r--r--org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/OptionsProposalProviderTest.java176
-rw-r--r--org.eclipse.mylyn.tasks.ui.tests/src/org/eclipse/mylyn/internal/tasks/ui/editors/LabelsAttributeEditorTest.java135
-rw-r--r--org.eclipse.mylyn.tasks.ui.tests/src/org/eclipse/mylyn/tasks/ui/editors/AttributeEditorToolkitTest.java132
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/OptionsProposalProvider.java84
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/editors/LabelsAttributeEditor.java88
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/editors/TextAttributeEditor.java6
-rw-r--r--org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/editors/AttributeEditorToolkit.java37
8 files changed, 650 insertions, 9 deletions
diff --git a/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/AllTasksTests.java b/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/AllTasksTests.java
index 4aab2e9ca..a819eb752 100644
--- a/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/AllTasksTests.java
+++ b/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/AllTasksTests.java
@@ -108,6 +108,7 @@ public class AllTasksTests {
suite.addTestSuite(TaskDataStoreTest.class);
suite.addTestSuite(TaskExportImportTest.class);
suite.addTestSuite(PersonProposalProviderTest.class);
+ suite.addTestSuite(OptionsProposalProviderTest.class);
suite.addTestSuite(TaskRepositoryLocationTest.class);
suite.addTestSuite(TaskRepositoryTest.class);
suite.addTestSuite(AttachmentSizeFormatterTest.class);
diff --git a/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/OptionsProposalProviderTest.java b/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/OptionsProposalProviderTest.java
new file mode 100644
index 000000000..04a24cb18
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/OptionsProposalProviderTest.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Tasktop Technologies and others.
+ * 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
+ *
+ * Contributors:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.tasks.tests;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.mylyn.internal.tasks.ui.OptionsProposalProvider;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+public class OptionsProposalProviderTest extends TestCase {
+
+ @Override
+ protected void setUp() throws Exception {
+ TaskTestUtil.resetTaskListAndRepositories();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ TaskTestUtil.resetTaskListAndRepositories();
+ }
+
+ @Test
+ public void testEmptyProposals() {
+ OptionsProposalProvider provider = new OptionsProposalProvider(new HashMap<String, String>(), true);
+ assertEquals(0, provider.getProposals("", 0).length);
+ assertEquals(0, provider.getProposals("", 10).length);
+ assertEquals(0, provider.getProposals("test", 0).length);
+ }
+
+ @Test
+ public void testSingleProposal() {
+ final Map<String, String> proposalMap = ImmutableMap.of("aTest", "1");
+ OptionsProposalProvider provider = new OptionsProposalProvider(proposalMap, true);
+
+ List<IContentProposal> proposals = Arrays.asList(provider.getProposals("", 0));
+ assertEquals(1, proposals.size());
+ assertProposal("aTest", "aTest", proposals.get(0));
+ }
+
+ @Test
+ public void testMultipleProposals() {
+ final Map<String, String> proposalMap = ImmutableMap.of("aTest", "1", "bTest", "2", "cTest", "3");
+ OptionsProposalProvider provider = new OptionsProposalProvider(proposalMap, true);
+
+ List<IContentProposal> proposals = Arrays.asList(provider.getProposals("", 0));
+ assertEquals(3, proposals.size());
+ assertProposal("aTest", "aTest", proposals.get(0));
+ assertProposal("bTest", "bTest", proposals.get(1));
+ assertProposal("cTest", "cTest", proposals.get(2));
+ }
+
+ @Test
+ public void testMultipleProposalsSorted() {
+ final Map<String, String> proposalMap = ImmutableMap.of("oneTest", "1", "twoTest", "2", "threeTest", "3");
+ OptionsProposalProvider provider = new OptionsProposalProvider(proposalMap, true);
+
+ List<IContentProposal> proposals = Arrays.asList(provider.getProposals("", 0));
+ assertEquals(3, proposals.size());
+ // NOTE: Sorted alphabetically by contents
+ assertProposal("oneTest", "oneTest", proposals.get(0));
+ assertProposal("threeTest", "threeTest", proposals.get(1));
+ assertProposal("twoTest", "twoTest", proposals.get(2));
+ }
+
+ @Test
+ public void testProposalFilterMultiSelect() {
+ final Map<String, String> proposalMap = ImmutableMap.of("OneTest", "1", "TwoTest", "2", "ThreeTest", "3");
+ OptionsProposalProvider provider = new OptionsProposalProvider(proposalMap, true);
+
+ assertEquals(0, provider.getProposals("ThreeTest", 0).length);
+
+ List<IContentProposal> proposals = Arrays.asList(provider.getProposals("ThreeTest,", 0));
+ assertEquals(2, proposals.size());
+ assertProposal("ThreeTest,OneTest", "OneTest", proposals.get(0));
+ assertProposal("ThreeTest,TwoTest", "TwoTest", proposals.get(1));
+
+ proposals = Arrays.asList(provider.getProposals("ThreeTest, ", 0));
+ assertEquals(2, proposals.size());
+ assertProposal("ThreeTest, OneTest", "OneTest", proposals.get(0));
+ assertProposal("ThreeTest, TwoTest", "TwoTest", proposals.get(1));
+
+ proposals = Arrays.asList(provider.getProposals("o", 0));
+ assertEquals(2, proposals.size());
+ assertProposal("OneTest", "OneTest", proposals.get(0));
+ assertProposal("TwoTest", "TwoTest", proposals.get(1));
+
+ proposals = Arrays.asList(provider.getProposals("O", 0));
+ assertEquals(2, proposals.size());
+ assertProposal("OneTest", "OneTest", proposals.get(0));
+ assertProposal("TwoTest", "TwoTest", proposals.get(1));
+
+ proposals = Arrays.asList(provider.getProposals("one", 0));
+ assertEquals(1, proposals.size());
+ assertProposal("OneTest", "OneTest", proposals.get(0));
+
+ assertEquals(0, provider.getProposals("four", 0).length);
+
+ proposals = Arrays.asList(provider.getProposals("four,", 0));
+ assertEquals(3, proposals.size());
+ assertProposal("four,OneTest", "OneTest", proposals.get(0));
+ assertProposal("four,ThreeTest", "ThreeTest", proposals.get(1));
+ assertProposal("four,TwoTest", "TwoTest", proposals.get(2));
+
+ proposals = Arrays.asList(provider.getProposals("four, ", 0));
+ assertEquals(3, proposals.size());
+ assertProposal("four, OneTest", "OneTest", proposals.get(0));
+ assertProposal("four, ThreeTest", "ThreeTest", proposals.get(1));
+ assertProposal("four, TwoTest", "TwoTest", proposals.get(2));
+
+ proposals = Arrays.asList(provider.getProposals(",, ,four, five , ", 0));
+ assertEquals(3, proposals.size());
+ assertProposal(",, ,four, five , OneTest", "OneTest", proposals.get(0));
+ assertProposal(",, ,four, five , ThreeTest", "ThreeTest", proposals.get(1));
+ assertProposal(",, ,four, five , TwoTest", "TwoTest", proposals.get(2));
+
+ proposals = Arrays.asList(provider.getProposals(",, ,four, five , one", 0));
+ assertEquals(1, proposals.size());
+ assertProposal(",, ,four, five , OneTest", "OneTest", proposals.get(0));
+ }
+
+ @Test
+ public void testProposalFilterSingleSelect() {
+ final Map<String, String> proposalMap = ImmutableMap.of("OneTest", "1", "TwoTest", "2", "ThreeTest", "3");
+ OptionsProposalProvider provider = new OptionsProposalProvider(proposalMap, false);
+
+ List<IContentProposal> proposals = Arrays.asList(provider.getProposals("ThreeTest", 0));
+ assertEquals(1, proposals.size());
+ assertProposal("ThreeTest", "ThreeTest", proposals.get(0));
+
+ assertEquals(0, provider.getProposals("ThreeTest,", 0).length);
+ assertEquals(0, provider.getProposals("ThreeTest, ", 0).length);
+
+ proposals = Arrays.asList(provider.getProposals("o", 0));
+ assertEquals(2, proposals.size());
+ assertProposal("OneTest", "OneTest", proposals.get(0));
+ assertProposal("TwoTest", "TwoTest", proposals.get(1));
+
+ proposals = Arrays.asList(provider.getProposals("O", 0));
+ assertEquals(2, proposals.size());
+ assertProposal("OneTest", "OneTest", proposals.get(0));
+ assertProposal("TwoTest", "TwoTest", proposals.get(1));
+
+ proposals = Arrays.asList(provider.getProposals("one", 0));
+ assertEquals(1, proposals.size());
+ assertProposal("OneTest", "OneTest", proposals.get(0));
+
+ assertEquals(0, provider.getProposals("four", 0).length);
+ assertEquals(0, provider.getProposals("four,", 0).length);
+ assertEquals(0, provider.getProposals("four, ", 0).length);
+ assertEquals(0, provider.getProposals(",, ,four, five , ", 0).length);
+ assertEquals(0, provider.getProposals(",, ,four, five , one", 0).length);
+ }
+
+ private void assertProposal(String content, String label, IContentProposal proposal) {
+ assertEquals(content, proposal.getContent());
+ assertEquals(label, proposal.getLabel());
+ assertNull(proposal.getDescription());
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui.tests/src/org/eclipse/mylyn/internal/tasks/ui/editors/LabelsAttributeEditorTest.java b/org.eclipse.mylyn.tasks.ui.tests/src/org/eclipse/mylyn/internal/tasks/ui/editors/LabelsAttributeEditorTest.java
new file mode 100644
index 000000000..36c73ac3e
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui.tests/src/org/eclipse/mylyn/internal/tasks/ui/editors/LabelsAttributeEditorTest.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Tasktop Technologies and others.
+ * 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
+ *
+ * Contributors:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.tasks.ui.editors;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.eclipse.mylyn.internal.tasks.core.data.TaskDataState;
+import org.eclipse.mylyn.tasks.core.TaskRepository;
+import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
+import org.eclipse.mylyn.tasks.core.data.TaskAttributeMapper;
+import org.eclipse.mylyn.tasks.core.data.TaskData;
+import org.eclipse.mylyn.tasks.core.data.TaskDataModel;
+import org.eclipse.mylyn.tasks.tests.TaskTestUtil;
+import org.eclipse.mylyn.tasks.tests.connector.MockTask;
+import org.eclipse.mylyn.tasks.ui.editors.LayoutHint.ColumnSpan;
+import org.eclipse.mylyn.tasks.ui.editors.LayoutHint.RowSpan;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class LabelsAttributeEditorTest {
+
+ private TaskData taskData;
+
+ private TaskDataModel model;
+
+ private LabelsAttributeEditor editor;
+
+ private TaskAttribute singleSelectAttribute;
+
+ private TaskAttribute multiSelectAttribute;
+
+ @Before
+ public void setUp() {
+ TaskRepository repository = TaskTestUtil.createMockRepository();
+ taskData = new TaskData(new TaskAttributeMapper(repository), "kind", "url", "id");
+ MockTask task = new MockTask("taskId");
+ TaskDataState state = new TaskDataState("kind", "url", "taskId");
+ state.setEditsData(taskData);
+ state.setLocalTaskData(taskData);
+ model = new TaskDataModel(repository, task, state);
+ }
+
+ @Test
+ public void testSingleSelectEmpty() {
+ createSingleSelect("");
+ assertSingleSelectValue("");
+ editor.setValue("test");
+ assertSingleSelectValue("test");
+ editor.setValue("");
+ assertSingleSelectValue("");
+ }
+
+ @Test
+ public void testSingleSelect() {
+ createSingleSelect("test option");
+ assertSingleSelectValue("test option");
+ editor.setValue("testing option");
+ assertSingleSelectValue("testing option");
+ }
+
+ @Test
+ public void testSingleSelectWithSeparator() {
+ createSingleSelect("one,two , three, four");
+ assertSingleSelectValue("one,two , three, four");
+ editor.setValue("one,two , three, four ,five ,");
+ assertSingleSelectValue("one,two , three, four ,five ,");
+ }
+
+ @Test
+ public void testMultiSelectEmpty() {
+ createMultiSelect(ImmutableList.<String> of());
+ assertMultiSelectValue("", ImmutableList.<String> of());
+ editor.setValue("one,two");
+ assertMultiSelectValue("one, two", ImmutableList.of("one", "two"));
+ editor.setValue("");
+ assertMultiSelectValue("", ImmutableList.<String> of());
+ }
+
+ @Test
+ public void testMultiSelect() {
+ createMultiSelect(ImmutableList.of("one", "two"));
+ assertMultiSelectValue("one, two", ImmutableList.of("one", "two"));
+ editor.setValue("one,two,three,four");
+ assertMultiSelectValue("one, two, three, four", ImmutableList.of("one", "two", "three", "four"));
+ editor.setValue("one");
+ assertMultiSelectValue("one", ImmutableList.of("one"));
+ editor.setValue("one,two , three, four ,five ,, ,");
+ assertMultiSelectValue("one, two, three, four, five", ImmutableList.of("one", "two", "three", "four", "five"));
+ }
+
+ private void createSingleSelect(String value) {
+ singleSelectAttribute = taskData.getRoot().createAttribute("singleSelect");
+ singleSelectAttribute.getMetaData().setType(TaskAttribute.TYPE_SINGLE_SELECT);
+ singleSelectAttribute.setValue(value);
+ editor = new LabelsAttributeEditor(model, singleSelectAttribute);
+ assertLayoutHint(false);
+ }
+
+ private void createMultiSelect(List<String> values) {
+ multiSelectAttribute = taskData.getRoot().createAttribute("multiSelect");
+ multiSelectAttribute.getMetaData().setType(TaskAttribute.TYPE_MULTI_SELECT);
+ multiSelectAttribute.setValues(ImmutableList.copyOf(values));
+ editor = new LabelsAttributeEditor(model, multiSelectAttribute);
+ assertLayoutHint(true);
+ }
+
+ private void assertSingleSelectValue(String value) {
+ assertEquals(value, editor.getValue());
+ assertEquals(value, singleSelectAttribute.getValue());
+ }
+
+ private void assertLayoutHint(boolean isMultiSelect) {
+ assertEquals(ColumnSpan.SINGLE, editor.getLayoutHint().columnSpan);
+ assertEquals((isMultiSelect ? RowSpan.MULTIPLE : RowSpan.SINGLE), editor.getLayoutHint().rowSpan);
+ }
+
+ private void assertMultiSelectValue(String stringValue, List<String> values) {
+ assertEquals(stringValue, editor.getValue());
+ assertEquals(values, multiSelectAttribute.getValues());
+ }
+
+}
diff --git a/org.eclipse.mylyn.tasks.ui.tests/src/org/eclipse/mylyn/tasks/ui/editors/AttributeEditorToolkitTest.java b/org.eclipse.mylyn.tasks.ui.tests/src/org/eclipse/mylyn/tasks/ui/editors/AttributeEditorToolkitTest.java
new file mode 100644
index 000000000..1ff7ac11b
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui.tests/src/org/eclipse/mylyn/tasks/ui/editors/AttributeEditorToolkitTest.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Tasktop Technologies and others.
+ * 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
+ *
+ * Contributors:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.tasks.ui.editors;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+import org.eclipse.mylyn.commons.workbench.WorkbenchUtil;
+import org.eclipse.mylyn.commons.workbench.editors.CommonTextSupport;
+import org.eclipse.mylyn.internal.tasks.ui.OptionsProposalProvider;
+import org.eclipse.mylyn.internal.tasks.ui.editors.LabelsAttributeEditor;
+import org.eclipse.mylyn.internal.tasks.ui.editors.MultiSelectionAttributeEditor;
+import org.eclipse.mylyn.internal.tasks.ui.editors.SingleSelectionAttributeEditor;
+import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
+import org.eclipse.mylyn.tasks.core.data.TaskData;
+import org.eclipse.mylyn.tasks.core.data.TaskDataModel;
+import org.eclipse.mylyn.tasks.tests.TaskTestUtil;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+public class AttributeEditorToolkitTest {
+ public class TestAttributeEditorToolkit extends AttributeEditorToolkit {
+
+ private ContentAssistCommandAdapter commandAdapter;
+
+ TestAttributeEditorToolkit(CommonTextSupport textSupport) {
+ super(textSupport);
+ }
+
+ @Override
+ public ContentAssistCommandAdapter createContentAssistCommandAdapter(Control control,
+ IContentProposalProvider proposalProvider) {
+ commandAdapter = super.createContentAssistCommandAdapter(control, proposalProvider);
+ return commandAdapter;
+ }
+
+ @Override
+ public IContentProposalProvider createContentProposalProvider(AbstractAttributeEditor editor) {
+ return super.createContentProposalProvider(editor);
+ }
+ }
+
+ private TestAttributeEditorToolkit toolkit;
+
+ private TaskAttribute taskAttribute;
+
+ private final FormToolkit formToolkit = new FormToolkit(Display.getCurrent());
+
+ private final TaskDataModel taskDataModel = mock(TaskDataModel.class);
+
+ @SuppressWarnings("restriction")
+ @Before
+ public void setUp() {
+ CommonTextSupport textSupport = mock(CommonTextSupport.class);
+ toolkit = spy(new TestAttributeEditorToolkit(textSupport));
+ TaskData taskData = TaskTestUtil.createMockTaskData("1");
+ taskAttribute = taskData.getRoot();
+ when(taskDataModel.getTaskData()).thenReturn(taskData);
+ }
+
+ @After
+ public void tearDown() {
+ formToolkit.dispose();
+ }
+
+ @Test
+ public void testAdaptSingleSelectionAttributeEditor() {
+ SingleSelectionAttributeEditor editor = new SingleSelectionAttributeEditor(taskDataModel, taskAttribute);
+ assertNoOptionsProposalProvider(editor);
+ }
+
+ @Test
+ public void testAdaptMultiSelectionAttributeEditor() {
+ MultiSelectionAttributeEditor editor = new MultiSelectionAttributeEditor(taskDataModel, taskAttribute);
+ assertNoOptionsProposalProvider(editor);
+ }
+
+ private void assertNoOptionsProposalProvider(AbstractAttributeEditor editor) {
+ editor.createControl(WorkbenchUtil.getShell(), formToolkit);
+ toolkit.adapt(editor);
+ verify(toolkit, never()).createContentProposalProvider(any(AbstractAttributeEditor.class));
+ verify(toolkit, never()).createContentAssistCommandAdapter(any(Control.class),
+ any(IContentProposalProvider.class));
+ }
+
+ @Test
+ public void testAdaptLabelsAttributeEditor() {
+ assertOptionsProposalProvider(true);
+ assertOptionsProposalProvider(false);
+ }
+
+ private void assertOptionsProposalProvider(boolean isMultiSelect) {
+ taskAttribute.getMetaData().setType(
+ isMultiSelect ? TaskAttribute.TYPE_MULTI_SELECT : TaskAttribute.TYPE_SINGLE_SELECT);
+ LabelsAttributeEditor editor = new LabelsAttributeEditor(taskDataModel, taskAttribute);
+ editor.createControl(WorkbenchUtil.getShell(), formToolkit);
+ toolkit.adapt(editor);
+
+ verify(toolkit).createContentProposalProvider(editor);
+ ArgumentCaptor<IContentProposalProvider> providerCaptor = ArgumentCaptor.forClass(IContentProposalProvider.class);
+ verify(toolkit).createContentAssistCommandAdapter(eq(editor.getControl()), providerCaptor.capture());
+ IContentProposalProvider proposalProvider = providerCaptor.getValue();
+ assertTrue(proposalProvider instanceof OptionsProposalProvider);
+ assertEquals(isMultiSelect, ((OptionsProposalProvider) proposalProvider).isMultiSelect());
+ assertNull(toolkit.commandAdapter.getAutoActivationCharacters());
+ }
+
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/OptionsProposalProvider.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/OptionsProposalProvider.java
new file mode 100644
index 000000000..574b26282
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/OptionsProposalProvider.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Tasktop Technologies and others.
+ * 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
+ *
+ * Contributors:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.tasks.ui;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jface.fieldassist.ContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+import org.eclipse.mylyn.internal.tasks.ui.editors.LabelsAttributeEditor;
+
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Ordering;
+
+public class OptionsProposalProvider implements IContentProposalProvider {
+
+ private static final String VALUE_SEPARATOR = ","; //$NON-NLS-1$
+
+ private final Set<String> proposals;
+
+ private final boolean isMultiSelect;
+
+ public OptionsProposalProvider(Map<String, String> proposals, boolean isMultiSelect) {
+ this.proposals = proposals.keySet();
+ this.isMultiSelect = isMultiSelect;
+ }
+
+ @Override
+ public IContentProposal[] getProposals(String contents, int position) {
+ Set<String> filteredProposals = new HashSet<>(proposals);
+ filteredProposals.remove(""); //$NON-NLS-1$
+ String lastValue = ""; //$NON-NLS-1$
+ // If the attribute is of type multi-select, filter the past values from the proposals
+ if (isMultiSelect) {
+ String[] contentsArray = contents.split(VALUE_SEPARATOR, -1);
+ if (contentsArray.length > 0) {
+ List<String> trimmedContents = LabelsAttributeEditor.getTrimmedValues(contentsArray);
+ filteredProposals.removeAll(trimmedContents);
+ lastValue = contentsArray[contentsArray.length - 1].trim();
+ }
+ } else {
+ lastValue = contents;
+ }
+
+ // If there is a last value, then filter the remaining the proposals to contain it
+ if (!lastValue.isEmpty()) {
+ for (Iterator<String> iterator = filteredProposals.iterator(); iterator.hasNext();) {
+ String proposal = iterator.next();
+ if (!proposal.toLowerCase().contains(lastValue.toLowerCase())) {
+ iterator.remove();
+ }
+
+ }
+ }
+ // Since the contents of the editor is replaced, we need to include the existing values in the replacement
+ final String existingValues = contents.substring(0, contents.length() - lastValue.length());
+ ImmutableList<String> sortedProposals = FluentIterable.from(filteredProposals).toSortedList(
+ Ordering.from(String.CASE_INSENSITIVE_ORDER));
+ return FluentIterable.from(sortedProposals).transform(new Function<String, IContentProposal>() {
+ public IContentProposal apply(String proposal) {
+ return new ContentProposal(existingValues + proposal, proposal, null);
+ }
+ }).toArray(IContentProposal.class);
+ }
+
+ public boolean isMultiSelect() {
+ return isMultiSelect;
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/editors/LabelsAttributeEditor.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/editors/LabelsAttributeEditor.java
new file mode 100644
index 000000000..d1a58c17a
--- /dev/null
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/editors/LabelsAttributeEditor.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Tasktop Technologies and others.
+ * 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
+ *
+ * Contributors:
+ * Tasktop Technologies - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.internal.tasks.ui.editors;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
+import org.eclipse.mylyn.tasks.core.data.TaskDataModel;
+import org.eclipse.mylyn.tasks.ui.editors.LayoutHint;
+import org.eclipse.mylyn.tasks.ui.editors.LayoutHint.ColumnSpan;
+import org.eclipse.mylyn.tasks.ui.editors.LayoutHint.RowSpan;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.base.Strings;
+import com.google.common.collect.FluentIterable;
+
+public class LabelsAttributeEditor extends TextAttributeEditor {
+
+ private static final String VALUE_SEPARATOR = ","; //$NON-NLS-1$
+
+ private final boolean isMultiSelect;
+
+ public LabelsAttributeEditor(TaskDataModel manager, TaskAttribute taskAttribute) {
+ super(manager, taskAttribute);
+ this.isMultiSelect = TaskAttribute.TYPE_MULTI_SELECT.equals(taskAttribute.getMetaData().getType());
+ if (!isReadOnly() && isMultiSelect) {
+ setLayoutHint(new LayoutHint(RowSpan.MULTIPLE, ColumnSpan.SINGLE));
+ }
+ }
+
+ @Override
+ public void createControl(Composite parent, FormToolkit toolkit) {
+ super.createControl(parent, toolkit, (isMultiSelect ? SWT.WRAP : SWT.NONE));
+ if (!isReadOnly() && isMultiSelect) {
+ getText().setToolTipText("Separate multiple values with a comma"); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public String getValue() {
+ if (isMultiSelect) {
+ List<String> values = getAttributeMapper().getValues(getTaskAttribute());
+ return Joiner.on(VALUE_SEPARATOR + " ").skipNulls().join(values); //$NON-NLS-1$
+ } else {
+ return getAttributeMapper().getValue(getTaskAttribute());
+ }
+ }
+
+ @Override
+ public void setValue(String text) {
+ if (isMultiSelect) {
+ String[] values = text.split(VALUE_SEPARATOR);
+ getAttributeMapper().setValues(getTaskAttribute(), getTrimmedValues(values));
+ } else {
+ getAttributeMapper().setValue(getTaskAttribute(), text);
+ }
+ attributeChanged();
+ }
+
+ public static List<String> getTrimmedValues(String[] values) {
+ return FluentIterable.from(Arrays.asList(values)).transform(new Function<String, String>() {
+ @Override
+ public String apply(String input) {
+ return Strings.nullToEmpty(input).trim();
+ }
+ }).filter(new Predicate<String>() {
+ @Override
+ public boolean apply(String input) {
+ return !Strings.isNullOrEmpty(input);
+ }
+ }).toList();
+ }
+}
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/editors/TextAttributeEditor.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/editors/TextAttributeEditor.java
index 5f4ea1e1d..6f6ed3238 100644
--- a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/editors/TextAttributeEditor.java
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/editors/TextAttributeEditor.java
@@ -45,6 +45,10 @@ public class TextAttributeEditor extends AbstractAttributeEditor {
@Override
public void createControl(Composite parent, FormToolkit toolkit) {
+ createControl(parent, toolkit, SWT.NONE);
+ }
+
+ protected void createControl(Composite parent, FormToolkit toolkit, int style) {
if (isReadOnly()) {
text = new Text(parent, SWT.FLAT | SWT.READ_ONLY);
text.setFont(EditorUtil.TEXT_FONT);
@@ -52,7 +56,7 @@ public class TextAttributeEditor extends AbstractAttributeEditor {
text.setToolTipText(getDescription());
text.setText(getValue());
} else {
- text = toolkit.createText(parent, getValue(), SWT.FLAT);
+ text = toolkit.createText(parent, getValue(), SWT.FLAT | style);
text.setFont(EditorUtil.TEXT_FONT);
text.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER);
text.setToolTipText(getDescription());
diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/editors/AttributeEditorToolkit.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/editors/AttributeEditorToolkit.java
index 47ac0b39f..b2a14c98a 100644
--- a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/editors/AttributeEditorToolkit.java
+++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/editors/AttributeEditorToolkit.java
@@ -28,9 +28,11 @@ import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.mylyn.commons.ui.compatibility.CommonThemes;
import org.eclipse.mylyn.commons.workbench.editors.CommonTextSupport;
+import org.eclipse.mylyn.internal.tasks.ui.OptionsProposalProvider;
import org.eclipse.mylyn.internal.tasks.ui.PersonProposalLabelProvider;
import org.eclipse.mylyn.internal.tasks.ui.PersonProposalProvider;
import org.eclipse.mylyn.internal.tasks.ui.editors.EditorUtil;
+import org.eclipse.mylyn.internal.tasks.ui.editors.LabelsAttributeEditor;
import org.eclipse.mylyn.internal.tasks.ui.editors.Messages;
import org.eclipse.mylyn.internal.tasks.ui.editors.PersonAttributeEditor;
import org.eclipse.mylyn.internal.tasks.ui.editors.RepositoryTextViewerConfiguration.Mode;
@@ -81,7 +83,16 @@ public class AttributeEditorToolkit {
}
public void adapt(AbstractAttributeEditor editor) {
- if (editor.getControl() instanceof Text || editor.getControl() instanceof CCombo
+ if (editor instanceof LabelsAttributeEditor) {
+ Control control = editor.getControl();
+ IContentProposalProvider contentProposalProvider = createContentProposalProvider(editor);
+ if (contentProposalProvider != null) {
+ ContentAssistCommandAdapter adapter = createContentAssistCommandAdapter(control,
+ contentProposalProvider);
+ adapter.setAutoActivationCharacters(null);
+ adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
+ }
+ } else if (editor.getControl() instanceof Text || editor.getControl() instanceof CCombo
|| editor instanceof PersonAttributeEditor) {
Control control = (editor instanceof PersonAttributeEditor)
? ((PersonAttributeEditor) editor).getText()
@@ -91,12 +102,11 @@ public class AttributeEditorToolkit {
control = editor.getControl();
}
if (!editor.isReadOnly() && hasContentAssist(editor.getTaskAttribute())) {
- IContentProposalProvider contentProposalProvider = createContentProposalProvider(editor.getTaskAttribute());
+ IContentProposalProvider contentProposalProvider = createContentProposalProvider(editor);
ILabelProvider labelPropsalProvider = createLabelProposalProvider(editor.getTaskAttribute());
if (contentProposalProvider != null && labelPropsalProvider != null) {
- ContentAssistCommandAdapter adapter = new ContentAssistCommandAdapter(control,
- getContentAdapter(control), contentProposalProvider,
- ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS, new char[0], true);
+ ContentAssistCommandAdapter adapter = createContentAssistCommandAdapter(control,
+ contentProposalProvider);
adapter.setLabelProvider(labelPropsalProvider);
adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
if (editor instanceof PersonAttributeEditor) {
@@ -127,6 +137,12 @@ public class AttributeEditorToolkit {
editor.decorate(getColorIncoming());
}
+ ContentAssistCommandAdapter createContentAssistCommandAdapter(Control control,
+ IContentProposalProvider proposalProvider) {
+ return new ContentAssistCommandAdapter(control, getContentAdapter(control), proposalProvider,
+ ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS, new char[0], true);
+ }
+
private IControlContentAdapter getContentAdapter(Control control) {
if (control instanceof Combo) {
return new ComboContentAdapter();
@@ -171,12 +187,17 @@ public class AttributeEditorToolkit {
/**
* Creates an IContentProposalProvider to provide content assist proposals for the given attribute.
*
- * @param attribute
- * attribute for which to provide content assist.
+ * @param editor
+ * editor for which to provide content assist.
* @return the IContentProposalProvider.
*/
- private IContentProposalProvider createContentProposalProvider(TaskAttribute attribute) {
+ IContentProposalProvider createContentProposalProvider(AbstractAttributeEditor editor) {
+ TaskAttribute attribute = editor.getTaskAttribute();
Map<String, String> proposals = attribute.getTaskData().getAttributeMapper().getOptions(attribute);
+ if (editor instanceof LabelsAttributeEditor) {
+ return new OptionsProposalProvider(proposals,
+ TaskAttribute.TYPE_MULTI_SELECT.equals(attribute.getMetaData().getType()));
+ }
return new PersonProposalProvider(null, attribute.getTaskData(), proposals);
}

Back to the top