/******************************************************************************* | |
* Copyright (c) 2011, 2013 Formal Mind GmbH and University of Dusseldorf. | |
* | |
* 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: | |
* Michael Jastram - initial API and implementation | |
******************************************************************************/ | |
package org.eclipse.rmf.reqif10.pror.editor.agilegrid; | |
import java.util.ArrayList; | |
import java.util.HashSet; | |
import java.util.List; | |
import java.util.Set; | |
import org.agilemore.agilegrid.AgileGrid; | |
import org.agilemore.agilegrid.Cell; | |
import org.agilemore.agilegrid.CellDoubleClickEvent; | |
import org.agilemore.agilegrid.CellResizeAdapter; | |
import org.agilemore.agilegrid.EditorActivationStrategy; | |
import org.agilemore.agilegrid.ICellDoubleClickListener; | |
import org.agilemore.agilegrid.ICellResizeListener; | |
import org.agilemore.agilegrid.ISelectionChangedListener; | |
import org.agilemore.agilegrid.SWTX; | |
import org.agilemore.agilegrid.SelectionChangedEvent; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.core.runtime.Status; | |
import org.eclipse.core.runtime.jobs.Job; | |
import org.eclipse.emf.common.command.Command; | |
import org.eclipse.emf.common.command.CompoundCommand; | |
import org.eclipse.emf.common.notify.Adapter; | |
import org.eclipse.emf.common.notify.AdapterFactory; | |
import org.eclipse.emf.common.notify.Notification; | |
import org.eclipse.emf.common.notify.impl.AdapterImpl; | |
import org.eclipse.emf.common.util.EList; | |
import org.eclipse.emf.ecore.EObject; | |
import org.eclipse.emf.ecore.EReference; | |
import org.eclipse.emf.ecore.util.EContentAdapter; | |
import org.eclipse.emf.edit.command.AddCommand; | |
import org.eclipse.emf.edit.command.SetCommand; | |
import org.eclipse.emf.edit.domain.EditingDomain; | |
import org.eclipse.emf.edit.ui.dnd.EditingDomainViewerDropAdapter; | |
import org.eclipse.emf.edit.ui.dnd.LocalTransfer; | |
import org.eclipse.emf.edit.ui.dnd.ViewerDragAdapter; | |
import org.eclipse.jface.action.MenuManager; | |
import org.eclipse.jface.util.LocalSelectionTransfer; | |
import org.eclipse.jface.viewers.ISelection; | |
import org.eclipse.jface.viewers.IStructuredSelection; | |
import org.eclipse.jface.viewers.StructuredSelection; | |
import org.eclipse.jface.viewers.StructuredViewer; | |
import org.eclipse.jface.viewers.Viewer; | |
import org.eclipse.rmf.reqif10.ReqIF10Factory; | |
import org.eclipse.rmf.reqif10.ReqIF10Package; | |
import org.eclipse.rmf.reqif10.SpecHierarchy; | |
import org.eclipse.rmf.reqif10.SpecObject; | |
import org.eclipse.rmf.reqif10.SpecRelation; | |
import org.eclipse.rmf.reqif10.Specification; | |
import org.eclipse.rmf.reqif10.common.util.ReqIF10Util; | |
import org.eclipse.rmf.reqif10.pror.configuration.Column; | |
import org.eclipse.rmf.reqif10.pror.configuration.ConfigurationPackage; | |
import org.eclipse.rmf.reqif10.pror.configuration.ProrSpecViewConfiguration; | |
import org.eclipse.rmf.reqif10.pror.editor.agilegrid.ProrRow.ProrRowSpecHierarchy; | |
import org.eclipse.rmf.reqif10.pror.editor.agilegrid.ProrRow.ProrRowSpecRelation; | |
import org.eclipse.rmf.reqif10.pror.filter.ReqifFilter; | |
import org.eclipse.rmf.reqif10.pror.util.ConfigurationUtil; | |
import org.eclipse.rmf.reqif10.pror.util.ProrUtil; | |
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.dnd.DND; | |
import org.eclipse.swt.dnd.DragSource; | |
import org.eclipse.swt.dnd.DragSourceEvent; | |
import org.eclipse.swt.dnd.DragSourceListener; | |
import org.eclipse.swt.dnd.DropTarget; | |
import org.eclipse.swt.dnd.DropTargetEvent; | |
import org.eclipse.swt.dnd.DropTargetListener; | |
import org.eclipse.swt.dnd.Transfer; | |
import org.eclipse.swt.events.KeyAdapter; | |
import org.eclipse.swt.events.KeyEvent; | |
import org.eclipse.swt.graphics.Cursor; | |
import org.eclipse.swt.graphics.Point; | |
import org.eclipse.swt.widgets.Composite; | |
import org.eclipse.swt.widgets.Control; | |
import org.eclipse.swt.widgets.Menu; | |
import org.eclipse.swt.widgets.Widget; | |
import org.eclipse.ui.part.PluginTransfer; | |
/** | |
* A Viewer that manages an {@link AgileGrid} rendering a | |
* {@link SpecHierarchyRoot}. | |
* | |
* @author jastram | |
*/ | |
public class ProrAgileGridViewer extends Viewer { | |
private final ProrAgileGrid agileGrid; | |
private Specification specification; | |
private ProrSpecViewConfiguration specViewConfig; | |
private ProrAgileGridContentProvider contentProvider; | |
private final EditingDomain editingDomain; | |
private IStructuredSelection selection; | |
private ISelectionChangedListener selectionChangedistener; | |
private EContentAdapter specHierarchyRootContentAdapter; | |
private Adapter emfColumnListener; | |
private ICellResizeListener agColumnListener; | |
private ICellDoubleClickListener doubleClickListener; | |
/** | |
* We need to ignore selection requests when a selection is in progress - | |
* see {@link #setSelection(ISelection, boolean)} | |
*/ | |
private boolean settingSelection = false; | |
/** | |
* We need to temporarily store the dragTarget in order to use the EMF | |
* DragNDrop Stuff. | |
*/ | |
protected SpecHierarchy dragTarget; | |
private EContentAdapter specRelationContentAdapter; | |
private AdapterFactory adapterFactory; | |
private AgileCellEditorActionHandler agileCellEditorActionHandler; | |
/** | |
* Constructs the Viewer. | |
* | |
* @param adapterFactory | |
*/ | |
public ProrAgileGridViewer(Composite composite, | |
AdapterFactory adapterFactory, EditingDomain editingDomain, | |
AgileCellEditorActionHandler agileCellEditorActionHandler) { | |
agileGrid = new ProrAgileGrid(composite, SWT.V_SCROLL | SWT.H_SCROLL | |
| SWTX.FILL_WITH_LASTCOL | SWT.MULTI | SWT.DOUBLE_BUFFERED); | |
agileGrid.setLayoutAdvisor(new ProrLayoutAdvisor(agileGrid)); | |
// agileGrid.setAgileGridEditor(new ProrAgileGridEditor(agileGrid)); | |
agileGrid.setEditorActivationStrategy(new EditorActivationStrategy( | |
agileGrid, true)); | |
this.editingDomain = editingDomain; | |
this.adapterFactory = adapterFactory; | |
this.agileCellEditorActionHandler = agileCellEditorActionHandler; | |
enableDragNDrop(); | |
enableKeyHandling(); | |
} | |
private void enableKeyHandling() { | |
agileGrid.addKeyListener(new KeyAdapter() { | |
@Override | |
public void keyPressed(KeyEvent e) { | |
if (e.stateMask == SWT.CTRL | |
&& (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR)) { | |
e.doit = false; | |
if (selection.isEmpty()) { | |
return; | |
} | |
Object obj = selection.getFirstElement(); | |
if (!(obj instanceof SpecHierarchy)) { | |
return; | |
} | |
SpecHierarchy specHierarchy = (SpecHierarchy) obj; | |
if (specHierarchy.getObject() == null) { | |
return; | |
} | |
insertNewRowAndEdit(specHierarchy); | |
} | |
} | |
}); | |
} | |
/** | |
* Inserts a new SpecHierarchy as the sibling of this one and starts editing | |
* it. | |
*/ | |
private void insertNewRowAndEdit(SpecHierarchy specHierarchy) { | |
// Won't do anything if there is no object | |
if (specHierarchy.getObject() == null | |
|| specHierarchy.getObject().getType() == null) { | |
return; | |
} | |
// Create the new elements | |
SpecHierarchy newSpecHierarchy = ReqIF10Factory.eINSTANCE | |
.createSpecHierarchy(); | |
SpecObject newSpecObject = ReqIF10Factory.eINSTANCE.createSpecObject(); | |
newSpecHierarchy.setObject(newSpecObject); | |
EReference childFeature; | |
int pos; | |
EObject parent = specHierarchy.eContainer(); | |
if (parent instanceof SpecHierarchy) { | |
childFeature = ReqIF10Package.Literals.SPEC_HIERARCHY__CHILDREN; | |
pos = ((SpecHierarchy) specHierarchy.eContainer()).getChildren() | |
.indexOf(specHierarchy) + 1; | |
} else if (parent instanceof Specification) { | |
childFeature = ReqIF10Package.Literals.SPECIFICATION__CHILDREN; | |
pos = ((Specification) specHierarchy.eContainer()).getChildren() | |
.indexOf(specHierarchy) + 1; | |
} else { | |
throw new IllegalStateException("Wrong parent: " + parent); | |
} | |
CompoundCommand cmd = ProrUtil.createAddTypedElementCommand(ReqIF10Util | |
.getReqIF(specHierarchy).getCoreContent(), | |
ReqIF10Package.Literals.REQ_IF_CONTENT__SPEC_OBJECTS, | |
newSpecObject, ReqIF10Package.Literals.SPEC_OBJECT__TYPE, | |
specHierarchy.getObject().getType(), -1, 0, editingDomain, | |
adapterFactory); | |
cmd.append(AddCommand.create(editingDomain, specHierarchy.eContainer(), | |
childFeature, newSpecHierarchy, pos)); | |
editingDomain.getCommandStack().execute(cmd); | |
// The row to be edited may be further down due to indenting. | |
Cell activeCell = agileGrid.getCellSelection()[0]; | |
int row = activeCell.row + 1; | |
ProrRow.ProrRowSpecHierarchy prorRowSpecHierarchy = (ProrRow.ProrRowSpecHierarchy) contentProvider | |
.getProrRow(row); | |
while (prorRowSpecHierarchy.getSpecHierarchy() != newSpecHierarchy | |
&& row <= contentProvider.getRowCount()) { | |
row++; | |
} | |
agileGrid.editCell(row, activeCell.column); | |
} | |
@Override | |
public Control getControl() { | |
return agileGrid; | |
} | |
@Override | |
public Object getInput() { | |
return specification; | |
} | |
@Override | |
public ISelection getSelection() { | |
return selection != null ? selection : StructuredSelection.EMPTY; | |
} | |
@Override | |
public void refresh() { | |
agileGrid.redraw(); | |
} | |
/** | |
* This method sets a {@link Specification} as input and registers some | |
* listeners. | |
*/ | |
@Override | |
public void setInput(Object input) { | |
unregisterColumnListener(); | |
unregisterSelectionChangedListener(); | |
unregisterSpecHierarchyListener(); | |
unregisterSpecRelationListener(); | |
unregisterDoubleClickListener(); | |
this.specification = (Specification) input; | |
this.specViewConfig = ConfigurationUtil.createSpecViewConfiguration( | |
specification, editingDomain); | |
this.contentProvider = new ProrAgileGridContentProvider(specification, | |
specViewConfig); | |
agileGrid.setContentProvider(contentProvider); | |
agileGrid.setCellRendererProvider(new ProrCellRendererProvider( | |
agileGrid, adapterFactory, editingDomain)); | |
agileGrid.setCellEditorProvider(new ProrCellEditorProvider(agileGrid, | |
adapterFactory, agileCellEditorActionHandler)); | |
agileGrid.setRowResizeCursor(new Cursor(agileGrid.getDisplay(), | |
SWT.CURSOR_ARROW)); | |
updateRowCount(); | |
updateColumnInformation(); | |
registerColumnListener(); | |
registerSelectionChangedListener(); | |
registerSpecHierarchyListener(); | |
registerSpecRelationListener(); | |
registerDoubleClickListener(); | |
resolveSpecObjectReferences(); | |
} | |
/** | |
* Turns out that it takes a lot of time to resolve the references from | |
* SpecHierarchy to Specification. This is usually done on demand. This is | |
* fine, but prevents fast scrolling the first time. Therefore, we do this | |
* as a background job. | |
*/ | |
private void resolveSpecObjectReferences() { | |
Job job = new Job("Resolving SpecObject References") { | |
@Override | |
protected IStatus run(IProgressMonitor monitor) { | |
HashSet<SpecHierarchy> specHierarchies = new HashSet<SpecHierarchy>(); | |
specHierarchies.addAll(specification.getChildren()); | |
monitor.beginTask("Resolving SpecObject References", | |
contentProvider.getRowCount()); | |
while (!specHierarchies.isEmpty()) { | |
SpecHierarchy specHierarchy = specHierarchies.iterator() | |
.next(); | |
specHierarchies.remove(specHierarchy); | |
specHierarchies.addAll(specHierarchy.getChildren()); | |
// This actually resolves the reference | |
specHierarchy.getObject(); | |
monitor.worked(1); | |
} | |
monitor.done(); | |
return Status.OK_STATUS; | |
} | |
}; | |
job.schedule(); | |
} | |
/** | |
* Explicit cleanup to prevent strange errors. | |
*/ | |
public void dispose() { | |
unregisterColumnListener(); | |
unregisterSelectionChangedListener(); | |
unregisterSpecHierarchyListener(); | |
unregisterSpecRelationListener(); | |
unregisterDoubleClickListener(); | |
if (!agileGrid.isDisposed()) { | |
agileGrid.dispose(); | |
} | |
} | |
private void unregisterColumnListener() { | |
if (emfColumnListener != null) | |
specViewConfig.eAdapters().remove(emfColumnListener); | |
} | |
/** | |
* Registers two listeners: (1) Listen to the EMF Model to propagate Column | |
* changes to the agileGrid; (2) Listen to the agileGrid and propagate | |
* changes to the EMF Model. | |
* <p> | |
* | |
* (1) Includes column count and labels.<br> | |
* (2) Includes column width. | |
*/ | |
private void registerColumnListener() { | |
emfColumnListener = new AdapterImpl() { | |
@Override | |
public void notifyChanged(Notification msg) { | |
if (!agileGrid.isDisposed()) { | |
updateColumnInformation(); | |
refresh(); | |
} | |
} | |
}; | |
specViewConfig.eAdapters().add(emfColumnListener); | |
agColumnListener = new CellResizeAdapter() { | |
@Override | |
public void columnResized(int col, int newWidth) { | |
// If the column index is -1 we resized the very first column, | |
// otherwise we resized a normal column | |
Column column = (col == -1) ? ConfigurationUtil | |
.getLeftHeaderColumn(specification, editingDomain) | |
: specViewConfig.getColumns().get(col); | |
if (column != null) { | |
Command cmd = SetCommand.create(editingDomain, column, | |
ConfigurationPackage.Literals.COLUMN__WIDTH, | |
newWidth); | |
editingDomain.getCommandStack().execute(cmd); | |
} | |
} | |
}; | |
agileGrid.addCellResizeListener(agColumnListener); | |
} | |
private void unregisterSpecHierarchyListener() { | |
if (specHierarchyRootContentAdapter != null) { | |
specification.eAdapters().remove(specHierarchyRootContentAdapter); | |
} | |
} | |
private void unregisterSpecRelationListener() { | |
if (specRelationContentAdapter != null) { | |
ReqIF10Util.getReqIF(specification).getCoreContent().eAdapters() | |
.remove(specRelationContentAdapter); | |
} | |
} | |
/** | |
* Attaches a Listener to notify the agileGrid when a | |
* {@link SpecHierarchyRoot} (or any of its children) changes. | |
*/ | |
private void registerSpecHierarchyListener() { | |
specHierarchyRootContentAdapter = new EContentAdapter() { | |
@Override | |
public void notifyChanged(Notification notification) { | |
super.notifyChanged(notification); | |
if (notification.getNewValue() instanceof SpecObject | |
&& notification.getEventType() == Notification.SET) { | |
SpecObject specObject = (SpecObject) notification | |
.getNewValue(); | |
contentProvider.updateElement(specObject); | |
} else if (notification.getFeature() == ReqIF10Package.Literals.SPECIFICATION__CHILDREN | |
|| notification.getFeature() == ReqIF10Package.Literals.SPEC_HIERARCHY__CHILDREN) { | |
updateRowCount(); | |
refresh(); | |
} | |
} | |
}; | |
specification.eAdapters().add(specHierarchyRootContentAdapter); | |
} | |
private void registerSpecRelationListener() { | |
specRelationContentAdapter = new EContentAdapter() { | |
@Override | |
public void notifyChanged(Notification notification) { | |
super.notifyChanged(notification); | |
SpecRelation specRelation = null; | |
if (notification.getNotifier() instanceof SpecRelation) { | |
specRelation = (SpecRelation) notification.getNotifier(); | |
} | |
if (notification.getNewValue() instanceof SpecRelation) { | |
specRelation = (SpecRelation) notification.getNewValue(); | |
} | |
if (specRelation != null) { | |
if (specRelation.getSource() != null) { | |
contentProvider.updateElement(specRelation.getSource()); | |
} | |
if (specRelation.getTarget() != null) { | |
contentProvider.updateElement(specRelation.getTarget()); | |
} | |
updateRowCount(); | |
// if (contentProvider.getShowSpecRelations()) { | |
// // By setting the flag again, the cash is cleared, | |
// // triggering a redraw. | |
// contentProvider.setShowSpecRelations(true); | |
// updateRowCount(); | |
// } | |
refresh(); | |
} | |
} | |
}; | |
ReqIF10Util.getReqIF(specification).getCoreContent().eAdapters() | |
.add(specRelationContentAdapter); | |
} | |
private void registerDoubleClickListener() { | |
doubleClickListener = new ICellDoubleClickListener() { | |
public void cellDoubleClicked(CellDoubleClickEvent event) { | |
Object source = event.getSource(); | |
if (source instanceof Cell) { | |
Cell cell = (Cell) source; | |
ProrRow prorRow = contentProvider.getProrRow(cell.row); | |
if (prorRow instanceof ProrRowSpecHierarchy | |
&& cell.column == specViewConfig.getColumns() | |
.size()) { | |
ProrRowSpecHierarchy prorRowSH = (ProrRowSpecHierarchy) prorRow; | |
prorRowSH.setShowSpecRelation(!prorRowSH | |
.isShowSpecRelation()); | |
contentProvider.flushCache(); | |
updateRowCount(); | |
refresh(); | |
} | |
} | |
} | |
}; | |
agileGrid.addCellDoubleClickListener(doubleClickListener); | |
} | |
private void unregisterDoubleClickListener() { | |
if (doubleClickListener != null && !agileGrid.isDisposed()) | |
agileGrid.removeDoubleClickListener(doubleClickListener); | |
} | |
private void unregisterSelectionChangedListener() { | |
if (selectionChangedistener != null && !agileGrid.isDisposed()) | |
agileGrid.removeSelectionChangedListener(selectionChangedistener); | |
} | |
/** | |
* Creates an {@link ISelectionChangedListener} for registration with the | |
* {@link AgileGrid} that relays Selections to the Eclipse environment. | |
* <p> | |
* | |
* What's confusing is that some of the AgileGrid stuff and Eclipse stuff | |
* has the same name (e.g. SelectionChangedEvent), so be aware of what's | |
* going on. | |
* | |
*/ | |
private void registerSelectionChangedListener() { | |
// Remove the old listener, if present | |
if (selectionChangedistener != null) { | |
agileGrid.removeSelectionChangedListener(selectionChangedistener); | |
} | |
// Create a Listener that translates the new selection from Cells to | |
// SpecHierarchies | |
selectionChangedistener = new ISelectionChangedListener() { | |
public void selectionChanged(SelectionChangedEvent event) { | |
if (settingSelection) { | |
return; | |
} | |
Set<Cell> cells = event.getNewSelections(); | |
List<Object> items = new ArrayList<Object>(); | |
for (Cell cell : cells) { | |
if (cell.row > -1) { | |
ProrRow row = contentProvider.getProrRow(cell.row); | |
// If the item is a SpecRelation and the last column is | |
// selected, show the target instead. | |
if (row instanceof ProrRowSpecRelation | |
&& cell.column == specViewConfig.getColumns() | |
.size()) { | |
SpecRelation relation = (SpecRelation) row | |
.getSpecElement(); | |
if (relation.getTarget() != null) { | |
items.add(relation.getTarget()); | |
} | |
} else if (row instanceof ProrRowSpecRelation | |
&& cell.column < specViewConfig.getColumns() | |
.size()) { | |
items.add(row.getSpecElement()); | |
} else if (row instanceof ProrRowSpecHierarchy) { | |
items.add(((ProrRowSpecHierarchy) row) | |
.getSpecHierarchy()); | |
} else { | |
throw new IllegalArgumentException(); | |
} | |
} | |
} | |
// If there are no items, it either means that (1) The user | |
// explicitly unselected everything, or (2) The user clicked in | |
// the empty space below the rows. In case of (2), we would like | |
// the Specification to be the current selection. | |
if (items.size() == 0) { | |
items.add(getInput()); | |
} | |
selection = new StructuredSelection(items); | |
ProrAgileGridViewer.this | |
.fireSelectionChanged(new org.eclipse.jface.viewers.SelectionChangedEvent( | |
ProrAgileGridViewer.this, selection)); | |
} | |
}; | |
agileGrid.addSelectionChangedListener(selectionChangedistener); | |
} | |
/** | |
* Updates the number so rows. This is an expensive operation, as we have to | |
* count the elements one by one. | |
*/ | |
private void updateRowCount() { | |
agileGrid.getLayoutAdvisor().setRowCount(contentProvider.getRowCount()); | |
} | |
/** | |
* Update Column Count, Column Label and Column Width. This should be called | |
* whenever a Column changes. | |
* <p> | |
* | |
*/ | |
private void updateColumnInformation() { | |
EList<Column> cols = specViewConfig.getColumns(); | |
Column leftHeaderColumn = ConfigurationUtil.getLeftHeaderColumn( | |
specification, editingDomain); | |
// Handle first column | |
if (leftHeaderColumn != null) | |
agileGrid.getLayoutAdvisor().setLeftHeaderWidth( | |
leftHeaderColumn.getWidth()); | |
// One more column for the links | |
if (!agileGrid.isDisposed()) { | |
agileGrid.getLayoutAdvisor().setColumnCount(cols.size() + 1); | |
for (int i = 0; i < cols.size(); i++) { | |
agileGrid.getLayoutAdvisor().setTopHeaderLabel(i, | |
cols.get(i).getLabel()); | |
agileGrid.getLayoutAdvisor().setColumnWidth(i, | |
cols.get(i).getWidth()); | |
} | |
} | |
} | |
/** | |
* Changes the selection. Note that we are only interested in the selected | |
* row, not column. Thus, if something is already selected in a certain row | |
* and the row is supposed to stay selected, then we'll leave the selection | |
* as is. | |
* <p> | |
* | |
* Further, we are only interested in SpecHierarchies and | |
* {@link SpecRelation}s. We tried to be smart by accepting SpecObjects as | |
* well (thereby selecting those SpecHierarchies linked to the SpecObject, | |
* but this is too expensive on large Specifications. | |
* <p> | |
*/ | |
@Override | |
public void setSelection(ISelection selection, boolean reveal) { | |
if (!(selection instanceof IStructuredSelection)) | |
return; | |
// Necessary due to a bug in CellSelectionManager (AgileGrid). | |
if (settingSelection) | |
return; | |
settingSelection = true; | |
this.selection = (IStructuredSelection) selection; | |
Set<Cell> cells = new HashSet<Cell>(); | |
for (int row = 0; row < contentProvider.getRowCount(); row++) { | |
ProrRow prorRow = contentProvider.getProrRow(row); | |
Object current; | |
if (prorRow instanceof ProrRow.ProrRowSpecHierarchy) | |
current = ((ProrRow.ProrRowSpecHierarchy) prorRow) | |
.getSpecHierarchy(); | |
else | |
current = prorRow.getSpecElement(); | |
for (Object item : ((IStructuredSelection) selection).toList()) { | |
if (item.equals(current)) { | |
boolean added = false; | |
for (int col = 0; col < agileGrid.getLayoutAdvisor() | |
.getColumnCount(); col++) { | |
if (agileGrid.isCellSelected(row, col)) { | |
cells.add(new Cell(agileGrid, row, col)); | |
added = true; | |
} | |
} | |
if (!added) { | |
cells.add(new Cell(agileGrid, row, 0)); | |
} | |
break; | |
} | |
} | |
} | |
Cell[] cellArray = new Cell[cells.size()]; | |
cells.toArray(cellArray); | |
// Without clearing the selection, selections would accumulate. | |
agileGrid.clearSelection(); | |
// Without focusing a cell, we'd get an overflow in CellSelectionManager | |
// due to a bug there. However, calling focusCell will trigger another | |
// selection event. This is a reason why we introduced #settingSelection | |
// to ignore further selection events when we are updating. | |
if (cellArray.length > 0) { | |
// We do not want to interrupt editing | |
if (!agileGrid.isCellEditorActive()) { | |
agileGrid.focusCell(cellArray[0]); | |
} | |
agileGrid.selectCells(cellArray); | |
org.eclipse.jface.viewers.SelectionChangedEvent event = new org.eclipse.jface.viewers.SelectionChangedEvent( | |
this, selection); | |
fireSelectionChanged(event); | |
} | |
// Notify all Listeners | |
settingSelection = false; | |
} | |
/** | |
* Can be called to provide a context menu | |
*/ | |
public void setContextMenu(MenuManager contextMenu) { | |
Menu menu = contextMenu.createContextMenu(agileGrid); | |
agileGrid.setMenu(menu); | |
} | |
private void enableDragNDrop() { | |
int dndOperations = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK; | |
Transfer[] transfers = new Transfer[] { LocalTransfer.getInstance(), | |
LocalSelectionTransfer.getTransfer(), | |
PluginTransfer.getInstance() }; | |
addDragSupport(dndOperations, transfers, new ViewerDragAdapter(this) { | |
// Modified to allow resizing of columns | |
@Override | |
public void dragStart(DragSourceEvent event) { | |
Cell cell = agileGrid.getCell(event.x, event.y); | |
if (cell.row == -1 || cell.column == -1) { | |
event.doit = false; | |
} | |
super.dragStart(event); | |
} | |
@Override | |
public void dragFinished(DragSourceEvent event) { | |
super.dragFinished(event); | |
} | |
}); | |
// Modified to make the EMF-Based Drag and Drop work. | |
addDropSupport(dndOperations, transfers, | |
new EditingDomainViewerDropAdapter(editingDomain, this) { | |
// store if previously hovered over a SpecHirarchy | |
protected boolean prevTargetIsSpecHierarchy; | |
protected SpecHierarchy prevTarget; | |
protected float prevLocation; | |
/** | |
* By using an AgileGrid instead of a StructuredViewer, item | |
* will always be null. Thus, we store the dragTarget when | |
* dragOver is called. | |
*/ | |
@Override | |
public void dragEnter(DropTargetEvent event) { | |
prevTargetIsSpecHierarchy = false; | |
prevTarget = null; | |
prevLocation = -1.0F; | |
super.dragEnter(event); | |
} | |
@Override | |
public void dragLeave(DropTargetEvent event) { | |
// clean up highlighting on leave and don't set | |
// dragTarget to null (still needed) | |
if (prevTargetIsSpecHierarchy) { | |
agileGrid.dndHoverCell = null; | |
// dragTarget = null; | |
agileGrid.redraw(); | |
prevTargetIsSpecHierarchy = false; | |
} | |
super.dragLeave(event); | |
} | |
@Override | |
protected Object extractDropTarget(Widget item) { | |
return dragTarget; | |
} | |
@Override | |
public void dragOver(DropTargetEvent e) { | |
Point pos = agileGrid.toControl(e.x, e.y); | |
Cell cell = agileGrid.getCell(pos.x, pos.y); | |
// No target if dragged over empty space. | |
if (cell.equals(Cell.NULLCELL)) { | |
dragTarget = null; | |
super.dragOver(e); | |
return; | |
} | |
ProrRow row = cell.row >= 0 ? contentProvider | |
.getProrRow(cell.row) : null; | |
Object target = null; | |
if (row instanceof ProrRowSpecHierarchy) { | |
target = ((ProrRowSpecHierarchy) row) | |
.getSpecHierarchy(); | |
} else if (row instanceof ProrRowSpecRelation) { | |
target = row.getSpecElement(); | |
} | |
if (target instanceof SpecHierarchy) { | |
dragTarget = (SpecHierarchy) target; | |
float location = getLocation(e); | |
// prevent flickering: redraw only if something | |
// changed | |
if ((location != prevLocation) | |
|| (dragTarget != prevTarget)) { | |
if (location == 0.5) { | |
agileGrid.dndHoverCell = cell; | |
agileGrid.dndHoverDropMode = ProrAgileGrid.DND_DROP_AS_CHILD; | |
} | |
if (location == 0.0) { | |
Cell prevCell = agileGrid.getNeighbor(cell, | |
AgileGrid.ABOVE, true); | |
agileGrid.dndHoverCell = prevCell; | |
agileGrid.dndHoverDropMode = ProrAgileGrid.DND_DROP_AS_SIBLING; | |
} | |
if (location == 1.0) { | |
agileGrid.dndHoverCell = cell; | |
agileGrid.dndHoverDropMode = ProrAgileGrid.DND_DROP_AS_SIBLING; | |
} | |
agileGrid.redraw(); | |
} | |
prevTargetIsSpecHierarchy = true; | |
prevTarget = dragTarget; | |
prevLocation = location; | |
} else { | |
// if previously hovered over a SpecHirarchy: redraw | |
// agileGrid | |
// once more to Reset highlighting and set | |
// dragTarget to null | |
if (prevTargetIsSpecHierarchy) { | |
agileGrid.dndHoverCell = null; | |
dragTarget = null; | |
agileGrid.redraw(); | |
prevTargetIsSpecHierarchy = false; | |
} | |
} | |
super.dragOver(e); | |
} | |
@Override | |
public void drop(DropTargetEvent event) { | |
super.drop(event); | |
agileGrid.dndHoverCell = null; | |
agileGrid.redraw(); | |
} | |
@Override | |
protected float getLocation(DropTargetEvent event) { | |
Point pos = agileGrid.toControl(event.x, event.y); | |
Cell cell = agileGrid.getCell(pos.x, pos.y); | |
if (agileGrid.getLayoutAdvisor() instanceof ProrLayoutAdvisor) { | |
ProrLayoutAdvisor layoutAdvisor = (ProrLayoutAdvisor) agileGrid | |
.getLayoutAdvisor(); | |
int rowHeight = layoutAdvisor | |
.getRowHeight(cell.row); | |
int y = agileGrid.getYForRow(cell.row); | |
int mouseY = pos.y - y; | |
float location = (float) mouseY / (float) rowHeight; | |
if (location < 0.3) { | |
return 0.0F; | |
} else if (location <= 0.7) { | |
return 0.5F; | |
} else { | |
return 1.0F; | |
} | |
} | |
return 1.0F; | |
} | |
}); | |
} | |
/** | |
* Copied from {@link StructuredViewer} | |
*/ | |
private void addDragSupport(int operations, Transfer[] transferTypes, | |
DragSourceListener listener) { | |
Control myControl = getControl(); | |
final DragSource dragSource = new DragSource(myControl, operations); | |
dragSource.setTransfer(transferTypes); | |
dragSource.addDragListener(listener); | |
} | |
/** | |
* Copied from {@link StructuredViewer} | |
*/ | |
private void addDropSupport(int operations, Transfer[] transferTypes, | |
final DropTargetListener listener) { | |
DropTarget dropTarget = new DropTarget(agileGrid, operations); | |
dropTarget.setTransfer(transferTypes); | |
dropTarget.addDropListener(listener); | |
} | |
public void setShowSpecRelations(boolean status) { | |
contentProvider.setShowSpecRelations(status); | |
updateRowCount(); | |
agileGrid.redraw(); | |
} | |
public void setFilter(ReqifFilter filter) { | |
if (agileGrid.getContentProvider() instanceof ProrAgileGridContentProvider) { | |
((ProrAgileGridContentProvider) agileGrid.getContentProvider()) | |
.setFilter(filter); | |
this.refresh(); | |
} | |
} | |
} |