blob: 3c9f5f8e833f94adfeddab143dd393e87af46779 [file] [log] [blame]
/*******************************************************************************
* 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();
}
}
}