/******************************************************************************* * Copyright (c) 2004, 2012 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 * David Green - fix for bug 254806, bug 267135 * Benjamin Muskalla - fix for bug 310798 *******************************************************************************/ package org.eclipse.mylyn.internal.tasks.ui.editors; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.mylyn.commons.ui.CommonImages; import org.eclipse.mylyn.internal.tasks.core.AbstractTaskCategory; import org.eclipse.mylyn.internal.tasks.core.ITaskList; import org.eclipse.mylyn.internal.tasks.ui.util.TaskContainerComparator; import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal; import org.eclipse.mylyn.internal.tasks.ui.views.TaskListView; import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector; import org.eclipse.mylyn.tasks.core.IRepositoryElement; import org.eclipse.mylyn.tasks.core.ITask; import org.eclipse.mylyn.tasks.core.TaskRepository; import org.eclipse.mylyn.tasks.core.data.AbstractTaskAttachmentHandler; import org.eclipse.mylyn.tasks.core.data.TaskAttribute; import org.eclipse.mylyn.tasks.core.data.TaskOperation; import org.eclipse.mylyn.tasks.ui.TasksUiImages; import org.eclipse.mylyn.tasks.ui.editors.AbstractAttributeEditor; import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPage; import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPart; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CCombo; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.custom.TextChangeListener; import org.eclipse.swt.custom.TextChangedEvent; import org.eclipse.swt.custom.TextChangingEvent; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.events.VerifyListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.Section; /** * @author Steffen Pingel */ public class TaskEditorActionPart extends AbstractTaskEditorPart { private static final String KEY_OPERATION = "operation"; //$NON-NLS-1$ public class SelectButtonListener implements ModifyListener, VerifyListener, SelectionListener, FocusListener, TextChangeListener { private final Button button; public SelectButtonListener(Button button) { this.button = button; } public void modifyText(ModifyEvent e) { selected(); } public void verifyText(VerifyEvent e) { selected(); } public void widgetSelected(SelectionEvent e) { selected(); } public void widgetDefaultSelected(SelectionEvent e) { selected(); } public void focusGained(FocusEvent event) { selected(); } public void focusLost(FocusEvent e) { } public void textChanged(TextChangedEvent event) { selected(); } public void textSet(TextChangedEvent event) { selected(); } public void textChanging(TextChangingEvent event) { } private void selected() { setSelectedRadionButton(button, true); } } private static final int DEFAULT_FIELD_WIDTH = 150; private static final int RADIO_OPTION_WIDTH = 120; private static final String KEY_ASSOCIATED_EDITOR = "associatedEditor"; //$NON-NLS-1$ private List<Button> operationButtons; private Button submitButton; private Button attachContextButton; private Button addToCategory; private CCombo categoryChooser; private AbstractTaskCategory category; private TaskAttribute selectedOperationAttribute; public TaskEditorActionPart() { setPartName(Messages.TaskEditorActionPart_Actions); } protected void addAttachContextButton(Composite buttonComposite, FormToolkit toolkit) { attachContextButton = toolkit.createButton(buttonComposite, Messages.TaskEditorActionPart_Attach_Context, SWT.CHECK); attachContextButton.setImage(CommonImages.getImage(TasksUiImages.CONTEXT_ATTACH)); } /** * Adds buttons to this composite. Subclasses can override this method to provide different/additional buttons. * * @param buttonComposite * Composite to add the buttons to. * @param toolkit */ private void createActionButtons(Composite buttonComposite, FormToolkit toolkit) { //if (!getTaskEditorPage().needsSubmitButton()) { submitButton = toolkit.createButton(buttonComposite, Messages.TaskEditorActionPart_Submit, SWT.NONE); submitButton.setImage(CommonImages.getImage(TasksUiImages.REPOSITORY_SUBMIT)); submitButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event e) { getTaskEditorPage().doSubmit(); } }); Point minSize = submitButton.computeSize(SWT.DEFAULT, SWT.DEFAULT, true); GridData submitButtonData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); submitButtonData.widthHint = Math.max(100, minSize.x); submitButton.setLayoutData(submitButtonData); setSubmitEnabled(true); toolkit.createLabel(buttonComposite, " "); //$NON-NLS-1$ //} createAttachContextButton(buttonComposite, toolkit); } private void createAttachContextButton(Composite buttonComposite, FormToolkit toolkit) { AbstractTaskEditorPage taskEditorPage = getTaskEditorPage(); AbstractRepositoryConnector connector = taskEditorPage.getConnector(); AbstractTaskAttachmentHandler taskAttachmentHandler = connector.getTaskAttachmentHandler(); boolean canPostContent = false; if (taskAttachmentHandler != null) { TaskRepository taskRepository = taskEditorPage.getTaskRepository(); ITask task = taskEditorPage.getTask(); canPostContent = taskAttachmentHandler.canPostContent(taskRepository, task); } if ((!getTaskData().isNew() && canPostContent)) { addAttachContextButton(buttonComposite, toolkit); } } /** * Creates the button layout. This displays options and buttons at the bottom of the editor to allow actions to be * performed on the bug. * * @param toolkit */ private void createCategoryChooser(Composite buttonComposite, FormToolkit toolkit) { addToCategory = getManagedForm().getToolkit().createButton(buttonComposite, Messages.TaskEditorActionPart_Add_to_Category, SWT.CHECK); categoryChooser = new CCombo(buttonComposite, SWT.FLAT | SWT.READ_ONLY); categoryChooser.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER); categoryChooser.setLayoutData(GridDataFactory.swtDefaults().hint(150, SWT.DEFAULT).create()); toolkit.adapt(categoryChooser, false, false); categoryChooser.setFont(TEXT_FONT); ITaskList taskList = TasksUiInternal.getTaskList(); final List<AbstractTaskCategory> categories = new ArrayList<AbstractTaskCategory>(taskList.getCategories()); Collections.sort(categories, new TaskContainerComparator()); AbstractTaskCategory selectedCategory = TasksUiInternal.getSelectedCategory(TaskListView.getFromActivePerspective()); int i = 0; int selectedIndex = 0; for (IRepositoryElement category : categories) { categoryChooser.add(category.getSummary()); if (category.equals(selectedCategory)) { selectedIndex = i; } i++; } categoryChooser.select(selectedIndex); categoryChooser.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent event) { if (categoryChooser.getSelectionIndex() != -1) { category = categories.get(categoryChooser.getSelectionIndex()); } } }); categoryChooser.setEnabled(false); addToCategory.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { markDirty(); if (!addToCategory.getSelection()) { category = null; } else if (categoryChooser.getSelectionIndex() != -1) { category = categories.get(categoryChooser.getSelectionIndex()); } categoryChooser.setEnabled(addToCategory.getSelection()); } }); GridDataFactory.fillDefaults() .hint(DEFAULT_FIELD_WIDTH, SWT.DEFAULT) .span(3, SWT.DEFAULT) .applyTo(categoryChooser); } public AbstractTaskCategory getCategory() { return category; } @Override public void createControl(Composite parent, FormToolkit toolkit) { Section section = createSection(parent, toolkit, true); Composite buttonComposite = toolkit.createComposite(section); GridLayout buttonLayout = EditorUtil.createSectionClientLayout(); buttonLayout.numColumns = 4; buttonComposite.setLayout(buttonLayout); if (getTaskEditorPage().needsAddToCategory()) { createCategoryChooser(buttonComposite, toolkit); } selectedOperationAttribute = getTaskData().getRoot().getMappedAttribute(TaskAttribute.OPERATION); if (selectedOperationAttribute != null && TaskAttribute.TYPE_OPERATION.equals(selectedOperationAttribute.getMetaData().getType())) { TaskOperation selectedOperation = getTaskData().getAttributeMapper().getTaskOperation( selectedOperationAttribute); createRadioButtons(buttonComposite, toolkit, selectedOperation); } createOperationAttributes(buttonComposite, toolkit); createActionButtons(buttonComposite, toolkit); toolkit.paintBordersFor(buttonComposite); section.setClient(buttonComposite); setSection(toolkit, section); } private void createOperationAttributes(Composite buttonComposite, FormToolkit toolkit) { Composite parent = null; for (TaskAttribute taskAttribute : getTaskData().getRoot().getAttributes().values()) { if (TaskAttribute.KIND_OPERATION.equals(taskAttribute.getMetaData().getKind())) { if (parent == null) { parent = toolkit.createComposite(buttonComposite); parent.setLayout(new GridLayout(2, false)); GridDataFactory.fillDefaults().span(4, 1).grab(true, false).applyTo(parent); toolkit.paintBordersFor(parent); } addAttribute(parent, toolkit, taskAttribute); } } } private void addAttribute(Composite attributesComposite, FormToolkit toolkit, TaskAttribute taskAttribute) { AbstractAttributeEditor attributeEditor = createAttributeEditor(taskAttribute); if (attributeEditor.hasLabel() && attributeEditor.getLabel().length() != 0) { attributeEditor.createLabelControl(attributesComposite, toolkit); Label label = attributeEditor.getLabelControl(); String text = label.getText(); label.setText(text); GridData gd = GridDataFactory.fillDefaults() .align(SWT.LEFT, SWT.CENTER) .hint(SWT.DEFAULT, SWT.DEFAULT) .create(); label.setLayoutData(gd); } attributeEditor.createControl(attributesComposite, toolkit); getTaskEditorPage().getAttributeEditorToolkit().adapt(attributeEditor); GridData gd = new GridData(GridData.FILL, GridData.CENTER, true, false); gd.minimumWidth = DEFAULT_FIELD_WIDTH; if (attributeEditor.getLabelControl() == null) { gd.horizontalSpan = 2; } attributeEditor.getControl().setLayoutData(gd); } private void addAttribute(Composite composite, FormToolkit toolkit, TaskAttribute attribute, Button button) { AbstractAttributeEditor editor = createAttributeEditor(attribute); if (editor != null) { editor.createControl(composite, toolkit); GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); gd.horizontalSpan = 3; Control editorControl = editor.getControl(); if (editorControl instanceof CCombo) { if (!Platform.OS_MACOSX.equals(Platform.getOS())) { // XXX on some platforms combo boxes are too tall by default and wider than other controls // bug 267135 only do this for non-mac platforms, since the default CCombo height on Carbon and Cocoa is perfect gd.heightHint = 20; } gd.widthHint = RADIO_OPTION_WIDTH; } else { gd.widthHint = RADIO_OPTION_WIDTH - 5; } editorControl.setLayoutData(gd); if (editor instanceof PersonAttributeEditor) { editorControl = ((PersonAttributeEditor) editor).getText(); } // the following listeners are hooked up so that changes to something in the actions area // will cause the corresponding radio button to become selected. Note that we can't just use // a focus listener due to bug 254806 if (editorControl instanceof CCombo) { ((CCombo) editorControl).addSelectionListener(new SelectButtonListener(button)); } else if (editorControl instanceof Text) { ((Text) editorControl).addModifyListener(new SelectButtonListener(button)); ((Text) editorControl).addVerifyListener(new SelectButtonListener(button)); } else if (editorControl instanceof StyledText) { ((StyledText) editorControl).getContent().addTextChangeListener(new SelectButtonListener(button)); } else { // last resort editorControl.addFocusListener(new SelectButtonListener(button)); } button.setData(KEY_ASSOCIATED_EDITOR, editor); getTaskEditorPage().getAttributeEditorToolkit().adapt(editor); } } private void createRadioButtons(Composite buttonComposite, FormToolkit toolkit, TaskOperation selectedOperation) { List<TaskOperation> operations = getTaskData().getAttributeMapper().getTaskOperations( selectedOperationAttribute); if (operations.size() > 0) { operationButtons = new ArrayList<Button>(); Button selectedButton = null; for (TaskOperation operation : operations) { Button button = toolkit.createButton(buttonComposite, operation.getLabel(), SWT.RADIO); button.setFont(TEXT_FONT); button.setData(KEY_OPERATION, operation); //button.setEnabled(!operation.getTaskAttribute().getMetaData().isReadOnly()); button.setEnabled(!operation.getTaskAttribute().getMetaData().isDisabled()); button.setToolTipText(operation.getTaskAttribute() .getMetaData() .getValue(TaskAttribute.META_DESCRIPTION)); GridData radioData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); TaskAttribute associatedAttribute = getTaskData().getAttributeMapper().getAssoctiatedAttribute( operation); if (associatedAttribute != null) { radioData.horizontalSpan = 1; addAttribute(buttonComposite, toolkit, associatedAttribute, button); } else { radioData.horizontalSpan = 4; } button.setLayoutData(radioData); button.addSelectionListener(new SelectButtonListener(button)); operationButtons.add(button); if (operation.equals(selectedOperation)) { selectedButton = button; } } // do this last to ensure only a single button is selected if (selectedButton == null && !operationButtons.isEmpty()) { selectedButton = operationButtons.get(0); } setSelectedRadionButton(selectedButton, false); } } public boolean getAttachContext() { if (attachContextButton == null || attachContextButton.isDisposed()) { return false; } else { return attachContextButton.getSelection(); } } // boolean needsAttachContext() { // return needsAttachContext; // } // // void setNeedsAttachContext(boolean attachContextEnabled) { // this.needsAttachContext = attachContextEnabled; // } public void setSubmitEnabled(boolean enabled) { if (submitButton != null && !submitButton.isDisposed()) { submitButton.setEnabled(enabled); if (enabled) { submitButton.setToolTipText(MessageFormat.format(Messages.TaskEditorActionPart_Submit_to_X, getTaskEditorPage().getTaskRepository().getRepositoryUrl())); } } } private void setSelectedRadionButton(Button selectedButton, boolean updateModel) { // avoid changes to the model if the button is already selected if (selectedButton.getSelection()) { return; } selectedButton.setSelection(true); for (Button button : operationButtons) { if (button != selectedButton) { button.setSelection(false); } } if (updateModel) { TaskOperation taskOperation = (TaskOperation) selectedButton.getData(KEY_OPERATION); getTaskData().getAttributeMapper().setTaskOperation(selectedOperationAttribute, taskOperation); getModel().attributeChanged(selectedOperationAttribute); AbstractAttributeEditor editor = (AbstractAttributeEditor) selectedButton.getData(KEY_ASSOCIATED_EDITOR); if (editor instanceof SingleSelectionAttributeEditor) { ((SingleSelectionAttributeEditor) editor).selectDefaultValue(); } } } /** * @since 3.5 */ public void refreshOperations() { if (operationButtons != null) { for (Button button : operationButtons) { TaskOperation taskOperation = (TaskOperation) button.getData(KEY_OPERATION); button.setEnabled(!taskOperation.getTaskAttribute().getMetaData().isDisabled()); button.setToolTipText(taskOperation.getTaskAttribute() .getMetaData() .getValue(TaskAttribute.META_DESCRIPTION)); } } } }