blob: d052ee2c1a472eef82ed567307e6a4355d4f581d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2014 Tasktop Technologies and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Tasktop Technologies - initial API and implementation
* Perforce - fixes for bug 318396
*******************************************************************************/
package org.eclipse.mylyn.internal.tasks.ui.editors;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.mylyn.commons.workbench.forms.CommonFormUtil;
import org.eclipse.mylyn.internal.tasks.ui.notifications.TaskDiffUtil;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal;
import org.eclipse.mylyn.internal.tasks.ui.views.UpdateRepositoryConfigurationAction;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.core.sync.TaskJob;
import org.eclipse.mylyn.tasks.ui.TasksUiImages;
import org.eclipse.mylyn.tasks.ui.editors.AbstractAttributeEditor;
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.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
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.PlatformUI;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.progress.IProgressConstants2;
/**
* Editor part that shows {@link TaskAttribute}s in a two column layout.
*
* @author Steffen Pingel
* @author Kevin Sawicki
*/
public abstract class AbstractTaskEditorAttributeSection extends AbstractTaskEditorSection {
private static final int LABEL_WIDTH = 110;
private static final int COLUMN_WIDTH = 140;
private static final int COLUMN_GAP = 20;
private static final int MULTI_COLUMN_WIDTH = COLUMN_WIDTH + 5 + COLUMN_GAP + LABEL_WIDTH + 5 + COLUMN_WIDTH;
private static final int MULTI_ROW_HEIGHT = 55;
private List<AbstractAttributeEditor> attributeEditors;
private boolean hasIncoming;
private Composite attributesComposite;
private boolean needsRefresh;
public AbstractTaskEditorAttributeSection() {
}
@Override
public void createControl(Composite parent, final FormToolkit toolkit) {
initialize();
super.createControl(parent, toolkit);
}
public boolean hasIncoming() {
return hasIncoming;
}
public void selectReveal(TaskAttribute attribute) {
if (attribute == null) {
return;
}
if (!getSection().isExpanded()) {
CommonFormUtil.setExpanded(getSection(), true);
}
EditorUtil.reveal(getTaskEditorPage().getManagedForm().getForm(), attribute.getId());
}
@Override
public boolean setFormInput(Object input) {
if (input instanceof String) {
String text = (String) input;
Collection<TaskAttribute> attributes = getAttributes();
for (TaskAttribute attribute : attributes) {
if (text.equals(attribute.getId())) {
selectReveal(attribute);
}
}
}
return super.setFormInput(input);
}
private void createAttributeControls(Composite attributesComposite, FormToolkit toolkit, int columnCount) {
int currentColumn = 1;
int currentPriority = 0;
for (AbstractAttributeEditor attributeEditor : attributeEditors) {
int priority = (attributeEditor.getLayoutHint() != null)
? attributeEditor.getLayoutHint().getPriority()
: LayoutHint.DEFAULT_PRIORITY;
if (priority != currentPriority) {
currentPriority = priority;
if (currentColumn > 1) {
while (currentColumn <= columnCount) {
getManagedForm().getToolkit().createLabel(attributesComposite, ""); //$NON-NLS-1$
currentColumn++;
}
currentColumn = 1;
}
}
if (attributeEditor.hasLabel()) {
attributeEditor.createLabelControl(attributesComposite, toolkit);
Label label = attributeEditor.getLabelControl();
String text = label.getText();
String shortenText = TaskDiffUtil.shortenText(label, text, LABEL_WIDTH);
label.setText(shortenText);
if (!text.equals(shortenText)) {
label.setToolTipText(text);
}
GridData gd = GridDataFactory.fillDefaults()
.align(SWT.RIGHT, SWT.CENTER)
.hint(LABEL_WIDTH, SWT.DEFAULT)
.create();
if (currentColumn > 1) {
gd.horizontalIndent = COLUMN_GAP;
gd.widthHint = LABEL_WIDTH + COLUMN_GAP;
}
label.setLayoutData(gd);
currentColumn++;
}
attributeEditor.createControl(attributesComposite, toolkit);
LayoutHint layoutHint = attributeEditor.getLayoutHint();
GridData gd = new GridData(SWT.FILL, SWT.CENTER, false, false);
RowSpan rowSpan = (layoutHint != null && layoutHint.rowSpan != null) ? layoutHint.rowSpan : RowSpan.SINGLE;
ColumnSpan columnSpan = (layoutHint != null && layoutHint.columnSpan != null)
? layoutHint.columnSpan
: ColumnSpan.SINGLE;
gd.horizontalIndent = 1;// prevent clipping of decorators on Windows
if (rowSpan == RowSpan.SINGLE && columnSpan == ColumnSpan.SINGLE) {
gd.widthHint = COLUMN_WIDTH;
gd.horizontalSpan = 1;
} else {
if (rowSpan == RowSpan.MULTIPLE) {
gd.heightHint = MULTI_ROW_HEIGHT;
}
if (columnSpan == ColumnSpan.SINGLE) {
gd.widthHint = COLUMN_WIDTH;
gd.horizontalSpan = 1;
} else {
gd.widthHint = MULTI_COLUMN_WIDTH;
gd.horizontalSpan = columnCount - currentColumn + 1;
}
}
attributeEditor.getControl().setLayoutData(gd);
getTaskEditorPage().getAttributeEditorToolkit().adapt(attributeEditor);
currentColumn += gd.horizontalSpan;
currentColumn %= columnCount;
}
}
private void initialize() {
attributeEditors = new ArrayList<AbstractAttributeEditor>();
hasIncoming = false;
Collection<TaskAttribute> attributes = getAttributes();
for (TaskAttribute attribute : attributes) {
AbstractAttributeEditor attributeEditor = createAttributeEditor(attribute);
if (attributeEditor != null) {
attributeEditors.add(attributeEditor);
if (getModel().hasIncomingChanges(attribute)) {
hasIncoming = true;
}
}
}
Comparator<AbstractAttributeEditor> attributeSorter = createAttributeEditorSorter();
if (attributeSorter != null) {
Collections.sort(attributeEditors, attributeSorter);
}
}
/**
* Create a comparator by which attribute editors will be sorted. By default attribute editors are sorted by layout
* hint priority. Subclasses may override this method to sort attribute editors in a custom way.
*
* @return comparator for {@link AbstractAttributeEditor} objects
*/
protected Comparator<AbstractAttributeEditor> createAttributeEditorSorter() {
return new Comparator<AbstractAttributeEditor>() {
public int compare(AbstractAttributeEditor o1, AbstractAttributeEditor o2) {
int p1 = (o1.getLayoutHint() != null) ? o1.getLayoutHint().getPriority() : LayoutHint.DEFAULT_PRIORITY;
int p2 = (o2.getLayoutHint() != null) ? o2.getLayoutHint().getPriority() : LayoutHint.DEFAULT_PRIORITY;
return p1 - p2;
}
};
}
@Override
protected Control createContent(FormToolkit toolkit, Composite parent) {
attributesComposite = toolkit.createComposite(parent);
attributesComposite.addListener(SWT.MouseDown, new Listener() {
public void handleEvent(Event event) {
Control focus = event.display.getFocusControl();
if (focus instanceof Text && ((Text) focus).getEditable() == false) {
getManagedForm().getForm().setFocus();
}
}
});
GridLayout attributesLayout = EditorUtil.createSectionClientLayout();
attributesLayout.numColumns = 4;
attributesLayout.horizontalSpacing = 9;
attributesLayout.verticalSpacing = 6;
attributesComposite.setLayout(attributesLayout);
GridData attributesData = new GridData(GridData.FILL_BOTH);
attributesData.horizontalSpan = 1;
attributesData.grabExcessVerticalSpace = false;
attributesComposite.setLayoutData(attributesData);
createAttributeControls(attributesComposite, toolkit, attributesLayout.numColumns);
toolkit.paintBordersFor(attributesComposite);
return attributesComposite;
}
protected IAction doCreateRefreshAction() {
UpdateRepositoryConfigurationAction repositoryConfigRefresh = new UpdateRepositoryConfigurationAction() {
@Override
public void run() {
getTaskEditorPage().showEditorBusy(true);
final TaskJob job = TasksUiInternal.getJobFactory().createUpdateRepositoryConfigurationJob(
getTaskEditorPage().getConnector(), getTaskEditorPage().getTaskRepository(),
getTaskEditorPage().getTask());
job.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
getTaskEditorPage().showEditorBusy(false);
if (job.getStatus() != null) {
getTaskEditorPage().getTaskEditor().setStatus(
Messages.TaskEditorAttributePart_Updating_of_repository_configuration_failed,
Messages.TaskEditorAttributePart_Update_Failed, job.getStatus());
} else {
getTaskEditorPage().refresh();
}
}
});
}
});
job.setUser(true);
// show the progress in the system task bar if this is a user job (i.e. forced)
job.setProperty(IProgressConstants2.SHOW_IN_TASKBAR_ICON_PROPERTY, Boolean.TRUE);
job.setPriority(Job.INTERACTIVE);
job.schedule();
};
};
repositoryConfigRefresh.setImageDescriptor(TasksUiImages.REPOSITORY_SYNCHRONIZE_SMALL);
repositoryConfigRefresh.selectionChanged(new StructuredSelection(getTaskEditorPage().getTaskRepository()));
repositoryConfigRefresh.setToolTipText(Messages.TaskEditorAttributePart_Refresh_Attributes);
return repositoryConfigRefresh;
}
@Override
protected void fillToolBar(ToolBarManager toolBar) {
if (needsRefresh()) {
IAction repositoryConfigRefresh = doCreateRefreshAction();
toolBar.add(repositoryConfigRefresh);
}
}
/**
* Returns the list of attributes that are show in the section.
*/
protected abstract Collection<TaskAttribute> getAttributes();
@Override
protected String getInfoOverlayText() {
StringBuilder sb = new StringBuilder();
List<TaskAttribute> overlayAttributes = getOverlayAttributes();
for (TaskAttribute attribute : overlayAttributes) {
String label = getModel().getTaskData().getAttributeMapper().getValueLabel(attribute);
if (label != null) {
if (sb.length() > 0) {
sb.append(" / "); //$NON-NLS-1$
}
}
sb.append(label);
}
return (sb.length() > 0) ? sb.toString() : null;
}
/**
* Returns the attributes that are shown in the overlay text.
*
* @see #getInfoOverlayText()
*/
protected abstract List<TaskAttribute> getOverlayAttributes();
protected boolean needsRefresh() {
return needsRefresh;
}
protected void setNeedsRefresh(boolean needsRefresh) {
this.needsRefresh = needsRefresh;
}
/**
* Integrator requested the ability to control whether the attributes section is expanded on creation.
*/
@Override
protected boolean shouldExpandOnCreate() {
return getTaskData().isNew() || hasIncoming;
}
}