/******************************************************************************* * Copyright (c) 2004, 2014 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 * Jeff Pound - attachment support * Frank Becker - improvements for bug 204051 *******************************************************************************/ package org.eclipse.mylyn.internal.tasks.ui.editors; import java.io.File; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.LegacyActionTools; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.IOpenListener; import org.eclipse.jface.viewers.OpenEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jface.window.ToolTip; import org.eclipse.mylyn.commons.core.CoreUtil; import org.eclipse.mylyn.commons.ui.CommonImages; import org.eclipse.mylyn.commons.ui.ConfigurableColumnTableViewerSupport; import org.eclipse.mylyn.commons.ui.TableColumnDescriptor; import org.eclipse.mylyn.commons.workbench.forms.CommonFormUtil; import org.eclipse.mylyn.internal.tasks.core.TaskAttachment; import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin; import org.eclipse.mylyn.internal.tasks.ui.commands.OpenTaskAttachmentHandler; import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiMenus; import org.eclipse.mylyn.internal.tasks.ui.views.TaskKeyComparator; import org.eclipse.mylyn.internal.tasks.ui.wizards.TaskAttachmentWizard.Mode; import org.eclipse.mylyn.tasks.core.ITaskAttachment; import org.eclipse.mylyn.tasks.core.data.TaskAttribute; import org.eclipse.mylyn.tasks.ui.TasksUiImages; import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPart; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; 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.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.forms.IManagedForm; import org.eclipse.ui.forms.events.ExpansionAdapter; import org.eclipse.ui.forms.events.ExpansionEvent; import org.eclipse.ui.forms.widgets.ExpandableComposite; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.forms.widgets.Section; /** * @author Mik Kersten * @author Rob Elves * @author Steffen Pingel */ public class TaskEditorAttachmentPart extends AbstractTaskEditorPart { private class AttachmentTableViewerComparator extends ViewerComparator { private int propertyIndex; private static final int DESCENDING = 1; private int direction = DESCENDING; public AttachmentTableViewerComparator() { this.propertyIndex = 0; direction = DESCENDING; } public void setColumn(int column) { if (column == this.propertyIndex) { // Same column as last sort; toggle the direction direction = 1 - direction; } else { // New column; do an ascending sort this.propertyIndex = column; direction = DESCENDING; } } @Override public int compare(Viewer viewer, Object e1, Object e2) { ITaskAttachment attachment1 = (ITaskAttachment) e1; ITaskAttachment attachment2 = (ITaskAttachment) e2; int rc; rc = compareColumn(attachment1, attachment2, propertyIndex); // If descending order, flip the direction if (direction == DESCENDING) { rc = -rc; } return rc; } } private class AttachmentTableFilter extends ViewerFilter { private boolean filterDeprecatedEnabled; public boolean isFilterDeprecatedEnabled() { return filterDeprecatedEnabled; } public void setFilterDeprecatedEnabled(boolean filterDeprecatedEnabled) { this.filterDeprecatedEnabled = filterDeprecatedEnabled; } @Override public boolean select(Viewer viewer, Object parentElement, Object element) { if (filterDeprecatedEnabled) { if (element instanceof ITaskAttachment) { return !((ITaskAttachment) element).isDeprecated(); } } return true; } } private static final String PREF_FILTER_DEPRECATED = "org.eclipse.mylyn.tasks.ui.editor.attachments.filter.deprecated"; //$NON-NLS-1$ private static final String ID_POPUP_MENU = "org.eclipse.mylyn.tasks.ui.editor.menu.attachments"; //$NON-NLS-1$ private List<TaskAttribute> attachmentAttributes; private boolean hasIncoming; private MenuManager menuManager; private Composite attachmentsComposite; private Table attachmentsTable; private AttachmentTableFilter tableFilter; private TableViewer attachmentsViewer; private List<ITaskAttachment> attachmentList; private Section section; private int nonDeprecatedCount; private Action filterDeprecatedAttachmentsAction; TaskKeyComparator keyComparator = new TaskKeyComparator(); private AttachmentTableViewerComparator comparator; public TaskEditorAttachmentPart() { setPartName(Messages.TaskEditorAttachmentPart_Attachments); } protected TableColumnDescriptor[] createColumnDescriptors() { TableColumnDescriptor[] descriptors = new TableColumnDescriptor[6]; descriptors[0] = new TableColumnDescriptor(130, Messages.TaskEditorAttachmentPart_Name, SWT.LEFT, false, SWT.None, false); descriptors[1] = new TableColumnDescriptor(150, Messages.TaskEditorAttachmentPart_Description, SWT.LEFT, false, SWT.None, false); descriptors[2] = new TableColumnDescriptor(70, Messages.TaskEditorAttachmentPart_Size, SWT.RIGHT, false, SWT.None, false); descriptors[3] = new TableColumnDescriptor(100, Messages.TaskEditorAttachmentPart_Creator, SWT.LEFT, false, SWT.None, true); descriptors[4] = new TableColumnDescriptor(100, Messages.TaskEditorAttachmentPart_Created, SWT.LEFT, true, SWT.DOWN, false); descriptors[5] = new TableColumnDescriptor(0, Messages.TaskEditorAttachmentPart_ID, SWT.LEFT, false, SWT.None, false); return descriptors; } private void createAttachmentTable(FormToolkit toolkit, final Composite attachmentsComposite) { attachmentsTable = toolkit.createTable(attachmentsComposite, SWT.MULTI | SWT.FULL_SELECTION); attachmentsTable.setLinesVisible(true); attachmentsTable.setHeaderVisible(true); attachmentsTable.setLayout(new GridLayout()); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).hint(500, SWT.DEFAULT).applyTo( attachmentsTable); attachmentsTable.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER); TableColumnDescriptor[] columnDescriptorArray = createColumnDescriptors(); String[] localAttachmentsColumns = new String[columnDescriptorArray.length]; for (int i = 0; i < columnDescriptorArray.length; i++) { int index = i; localAttachmentsColumns[i] = columnDescriptorArray[i].getName(); TableColumn column = new TableColumn(attachmentsTable, columnDescriptorArray[i].getAlignment(), index); column.setText(columnDescriptorArray[i].getName()); column.setWidth(columnDescriptorArray[i].getWidth()); column.setMoveable(true); column.setData(TableColumnDescriptor.TABLE_COLUMN_DESCRIPTOR_KEY, columnDescriptorArray[i]); if (columnDescriptorArray[i].isDefaultSortColumn()) { attachmentsTable.setSortColumn(column); attachmentsTable.setSortDirection(columnDescriptorArray[i].getSortDirection()); } column.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { comparator.setColumn(index); } }); } attachmentsViewer = new TableViewer(attachmentsTable); attachmentsViewer.setUseHashlookup(true); attachmentsViewer.setColumnProperties(localAttachmentsColumns); ColumnViewerToolTipSupport.enableFor(attachmentsViewer, ToolTip.NO_RECREATE); comparator = createComparator(); attachmentsViewer.setComparator(comparator); attachmentsViewer.setContentProvider(new ArrayContentProvider()); attachmentsViewer.setLabelProvider(createTableProvider()); attachmentsViewer.addOpenListener(new IOpenListener() { public void open(OpenEvent event) { openAttachments(event); } }); attachmentsViewer.addSelectionChangedListener(getTaskEditorPage()); attachmentsViewer.setInput(attachmentList.toArray()); menuManager = new MenuManager(); menuManager.setRemoveAllWhenShown(true); menuManager.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { TasksUiMenus.fillTaskAttachmentMenu(manager); } }); getTaskEditorPage().getEditorSite().registerContextMenu(ID_POPUP_MENU, menuManager, attachmentsViewer, true); Menu menu = menuManager.createContextMenu(attachmentsTable); attachmentsTable.setMenu(menu); attachmentsViewer.addFilter(tableFilter); new ConfigurableColumnTableViewerSupport(attachmentsViewer, columnDescriptorArray, getStateFile()); } private File getStateFile() { IPath stateLocation = Platform.getStateLocation(TasksUiPlugin.getDefault().getBundle()); return stateLocation.append("TaskEditorAttachmentPart.xml").toFile(); //$NON-NLS-1$ } private void createButtons(Composite attachmentsComposite, FormToolkit toolkit) { final Composite attachmentControlsComposite = toolkit.createComposite(attachmentsComposite); attachmentControlsComposite.setLayout(new GridLayout(2, false)); attachmentControlsComposite.setLayoutData(new GridData(GridData.BEGINNING)); Button attachFileButton = toolkit.createButton(attachmentControlsComposite, Messages.TaskEditorAttachmentPart_Attach_, SWT.PUSH); attachFileButton.setImage(CommonImages.getImage(CommonImages.FILE_PLAIN)); attachFileButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { EditorUtil.openNewAttachmentWizard(getTaskEditorPage(), Mode.DEFAULT, null); } }); getTaskEditorPage().registerDefaultDropListener(attachFileButton); Button attachScreenshotButton = toolkit.createButton(attachmentControlsComposite, Messages.TaskEditorAttachmentPart_Attach__Screenshot, SWT.PUSH); attachScreenshotButton.setImage(CommonImages.getImage(CommonImages.IMAGE_CAPTURE)); attachScreenshotButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { EditorUtil.openNewAttachmentWizard(getTaskEditorPage(), Mode.SCREENSHOT, null); } }); getTaskEditorPage().registerDefaultDropListener(attachScreenshotButton); } @Override public void createControl(Composite parent, final FormToolkit toolkit) { initialize(); section = createSection(parent, toolkit, hasIncoming); updateSectionTitle(); if (hasIncoming) { expandSection(toolkit, section); } else { section.addExpansionListener(new ExpansionAdapter() { @Override public void expansionStateChanged(ExpansionEvent event) { if (attachmentsComposite == null) { expandSection(toolkit, section); getTaskEditorPage().reflow(); } } }); } setSection(toolkit, section); } private void expandSection(FormToolkit toolkit, Section section) { attachmentsComposite = toolkit.createComposite(section); attachmentsComposite.setLayout(EditorUtil.createSectionClientLayout()); attachmentsComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); getTaskEditorPage().registerDefaultDropListener(section); if (attachmentAttributes.size() > 0) { createAttachmentTable(toolkit, attachmentsComposite); } else { Label label = toolkit.createLabel(attachmentsComposite, Messages.TaskEditorAttachmentPart_No_attachments); getTaskEditorPage().registerDefaultDropListener(label); } createButtons(attachmentsComposite, toolkit); toolkit.paintBordersFor(attachmentsComposite); section.setClient(attachmentsComposite); } @Override public void dispose() { if (menuManager != null) { menuManager.dispose(); } super.dispose(); } private void initialize() { attachmentAttributes = getTaskData().getAttributeMapper().getAttributesByType(getTaskData(), TaskAttribute.TYPE_ATTACHMENT); attachmentList = new ArrayList<ITaskAttachment>(attachmentAttributes.size()); for (TaskAttribute attribute : attachmentAttributes) { if (getModel().hasIncomingChanges(attribute)) { hasIncoming = true; } TaskAttachment taskAttachment = new TaskAttachment(getModel().getTaskRepository(), getModel().getTask(), attribute); getTaskData().getAttributeMapper().updateTaskAttachment(taskAttachment, attribute); attachmentList.add(taskAttachment); if (!taskAttachment.isDeprecated()) { nonDeprecatedCount++; } } tableFilter = new AttachmentTableFilter(); } @Override protected void fillToolBar(ToolBarManager toolBarManager) { filterDeprecatedAttachmentsAction = new Action("", IAction.AS_CHECK_BOX) { //$NON-NLS-1$ @Override public void run() { TasksUiPlugin.getDefault().getPreferenceStore().setValue(PREF_FILTER_DEPRECATED, isChecked()); filterDeprecated(isChecked()); } }; filterDeprecatedAttachmentsAction.setImageDescriptor(TasksUiImages.FILTER_OBSOLETE_SMALL); filterDeprecatedAttachmentsAction.setToolTipText(Messages.TaskEditorAttachmentPart_Hide_Obsolete_Tooltip); if (nonDeprecatedCount > 0 && nonDeprecatedCount < attachmentAttributes.size()) { filterDeprecated(TasksUiPlugin.getDefault().getPreferenceStore().getBoolean(PREF_FILTER_DEPRECATED)); } else { // do not allow filtering if it would cause the table to be empty or no change filterDeprecatedAttachmentsAction.setEnabled(false); } toolBarManager.add(filterDeprecatedAttachmentsAction); Action attachFileAction = new Action() { @Override public void run() { EditorUtil.openNewAttachmentWizard(getTaskEditorPage(), Mode.DEFAULT, null); } }; attachFileAction.setToolTipText(Messages.TaskEditorAttachmentPart_Attach_); attachFileAction.setImageDescriptor(TasksUiImages.FILE_NEW_SMALL); toolBarManager.add(attachFileAction); Action attachScreenshotAction = new Action() { @Override public void run() { EditorUtil.openNewAttachmentWizard(getTaskEditorPage(), Mode.SCREENSHOT, null); } }; attachScreenshotAction.setToolTipText(Messages.TaskEditorAttachmentPart_Attach__Screenshot); attachScreenshotAction.setImageDescriptor(TasksUiImages.IMAGE_CAPTURE_SMALL); toolBarManager.add(attachScreenshotAction); } private void updateSectionTitle() { if (tableFilter.isFilterDeprecatedEnabled()) { section.setText(NLS.bind(Messages.TaskEditorAttachmentPart_Attachment_Section_Title_X_of_Y, new Object[] { LegacyActionTools.escapeMnemonics(getPartName()), nonDeprecatedCount, attachmentAttributes.size() })); } else { section.setText(NLS.bind(Messages.TaskEditorAttachmentPart_Attachment_Section_Title_X, LegacyActionTools.escapeMnemonics(getPartName()), attachmentAttributes.size())); } } protected void openAttachments(OpenEvent event) { List<ITaskAttachment> attachments = new ArrayList<ITaskAttachment>(); StructuredSelection selection = (StructuredSelection) event.getSelection(); List<?> items = selection.toList(); for (Object item : items) { if (item instanceof ITaskAttachment) { attachments.add((ITaskAttachment) item); } } if (attachments.isEmpty()) { return; } IWorkbenchPage page = getTaskEditorPage().getSite().getWorkbenchWindow().getActivePage(); try { OpenTaskAttachmentHandler.openAttachments(page, attachments); } catch (OperationCanceledException e) { // canceled } } @Override public boolean setFormInput(Object input) { if (input instanceof String) { String text = (String) input; if (attachmentAttributes != null) { for (ITaskAttachment attachment : attachmentList) { if (text.equals(attachment.getTaskAttribute().getId())) { CommonFormUtil.setExpanded((ExpandableComposite) getControl(), true); return selectReveal(attachment); } } } } return super.setFormInput(input); } private boolean selectReveal(ITaskAttachment attachment) { if (attachment == null || attachmentsTable == null) { return false; } if (tableFilter.isFilterDeprecatedEnabled() && attachment.isDeprecated()) { filterDeprecated(false); } attachmentsViewer.setSelection(new StructuredSelection(attachment)); IManagedForm mform = getManagedForm(); ScrolledForm form = mform.getForm(); EditorUtil.focusOn(form, attachmentsTable); return true; } void filterDeprecated(boolean filter) { if (filterDeprecatedAttachmentsAction.isChecked() != filter) { filterDeprecatedAttachmentsAction.setChecked(filter); } tableFilter.setFilterDeprecatedEnabled(filter); if (attachmentsViewer != null) { attachmentsViewer.refresh(); getTaskEditorPage().reflow(); } updateSectionTitle(); } protected int compareColumn(ITaskAttachment attachment1, ITaskAttachment attachment2, int propertyIndex) { int rc; switch (propertyIndex) { case 0: rc = CoreUtil.compare(attachment1.getFileName(), attachment2.getFileName()); break; case 1: String description1 = attachment1.getDescription(); String description2 = attachment2.getDescription(); rc = CoreUtil.compare(description1, description2); break; case 2: rc = CoreUtil.compare(attachment1.getLength(), attachment2.getLength()); break; case 3: String author1 = attachment1.getAuthor() != null ? attachment1.getAuthor().toString() : null; String author2 = attachment2.getAuthor() != null ? attachment2.getAuthor().toString() : null; rc = CoreUtil.compare(author1, author2); break; case 4: rc = CoreUtil.compare(attachment1.getCreationDate(), attachment2.getCreationDate()); break; case 5: String key1 = AttachmentTableLabelProvider.getAttachmentId(attachment1); String key2 = AttachmentTableLabelProvider.getAttachmentId(attachment2); rc = keyComparator.compare2(key1, key2); break; default: rc = 0; break; } return rc; } protected AttachmentTableViewerComparator createComparator() { return new AttachmentTableViewerComparator(); } protected AttachmentTableLabelProvider createTableProvider() { return new AttachmentTableLabelProvider(); } }