{
final SapphirePart part = ( (PartEvent) event ).part();
if( part instanceof MasterDetailsContentNodePart )
{
final MasterDetailsContentNodePart node = (MasterDetailsContentNodePart) part;
if( event instanceof PartVisibilityEvent )
{
final MasterDetailsContentNodePart parent = node.getParentNode();
if( parent == outline.getRoot() )
{
treeViewer.refresh();
}
else
{
treeViewer.refresh( parent );
}
}
else
{
if( node.visible() )
{
if( event instanceof LabelChangedEvent || event instanceof ImageChangedEvent || event instanceof DecorationEvent )
{
Display.getCurrent().asyncExec( new TreeViewerUpdateJob( treeViewer, node ) );
}
else if( event instanceof NodeListEvent )
{
treeViewer.refresh( node );
}
}
}
}
}
}
};
private void attach( final List<MasterDetailsContentNodePart> nodes )
{
for( MasterDetailsContentNodePart node : nodes )
{
node.attach( this.listener );
}
}
private void detach( final List<MasterDetailsContentNodePart> nodes )
{
for( MasterDetailsContentNodePart node : nodes )
{
node.detach( this.listener );
detach( node.nodes() );
}
}
public Object[] getElements( final Object inputElement )
{
final MasterDetailsContentNodeList nodes = outline.getRoot().nodes();
attach( nodes );
return nodes.visible().toArray();
}
public Object[] getChildren( final Object parentElement )
{
final MasterDetailsContentNodeList nodes = ( (MasterDetailsContentNodePart) parentElement ).nodes();
attach( nodes );
return nodes.visible().toArray();
}
public Object getParent( final Object element )
{
return ( (MasterDetailsContentNodePart) element ).getParentNode();
}
public boolean hasChildren( final Object parentElement )
{
final MasterDetailsContentNodeList nodes = ( (MasterDetailsContentNodePart) parentElement ).nodes();
attach( nodes );
return ! nodes.visible().isEmpty();
}
public void inputChanged( final Viewer viewer,
final Object oldInput,
final Object newInput )
{
}
public void dispose()
{
detach( outline.getRoot().nodes() );
}
};
final StyledCellLabelProvider labelProvider = new StyledCellLabelProvider ()
{
private final Map<ImageDescriptor,Image> images = new HashMap<ImageDescriptor,Image>();
private final Map<org.eclipse.sapphire.Color,Color> colors = new HashMap<org.eclipse.sapphire.Color,Color>();
public void update( final ViewerCell cell )
{
final MasterDetailsContentNodePart node = (MasterDetailsContentNodePart) cell.getElement();
final StyledString styledString = new StyledString( node.getLabel() );
for( final TextDecoration decoration : node.decorations() )
{
String text = decoration.text();
if( text != null )
{
text = text.trim();
if( text.length() > 0 )
{
final Color color = color( decoration.color() );
styledString.append
(
" " + text,
new Styler()
{
@Override
public void applyStyles( final TextStyle style )
{
style.foreground = color;
}
}
);
}
}
}
cell.setText( styledString.toString() );
cell.setStyleRanges( styledString.getStyleRanges() );
cell.setImage( image( node.getImage() ) );
super.update( cell );
}
private Color color( final org.eclipse.sapphire.Color c )
{
Color color = this.colors.get( c );
if( color == null )
{
color = new Color( tree.getDisplay(), c.red(), c.green(), c.blue() );
this.colors.put( c, color );
}
return color;
}
private Image image( final ImageDescriptor imageDescriptor )
{
Image image = this.images.get( imageDescriptor );
if( image == null )
{
image = imageDescriptor.createImage();
this.images.put( imageDescriptor, image );
}
return image;
}
@Override
public void dispose()
{
for( final Image image : this.images.values() )
{
image.dispose();
}
}
};
new SapphireToolTip( tree )
{
protected Object getToolTipArea( final Event event )
{
return treeViewer.getCell( new Point( event.x, event.y ) );
}
protected boolean shouldCreateToolTip(Event event)
{
if( ! super.shouldCreateToolTip( event ) )
{
return false;
}
setShift( new Point( 0, 20 ) );
tree.setToolTipText( "" );
boolean res = false;
final MasterDetailsContentNodePart node = getNode( event );
if( node != null )
{
res = ! node.validation().ok();
}
return res;
}
private MasterDetailsContentNodePart getNode( final Event event )
{
final TreeItem item = tree.getItem( new Point(event.x, event.y) );
if( item == null )
{
return null;
}
else
{
return (MasterDetailsContentNodePart) item.getData();
}
}
protected void afterHideToolTip(Event event) {
super.afterHideToolTip(event);
// Clear the restored value else this could be a source of a leak
if (event != null && event.widget != treeViewer.getControl()) {
treeViewer.getControl().setFocus();
}
}
@Override
protected void createContent( final Event event,
final Composite parent )
{
final MasterDetailsContentNodePart node = getNode( event );
parent.setLayout( glayout( 1 ) );
SapphireFormText text = new SapphireFormText( parent, SWT.NO_FOCUS );
text.setLayoutData( gdfill() );
final org.eclipse.sapphire.modeling.Status validation = node.validation();
final List<org.eclipse.sapphire.modeling.Status> items = gather( validation );
final StringBuffer buffer = new StringBuffer();
buffer.append( "<form>" );
final int count = items.size();
int i = 0;
for( org.eclipse.sapphire.modeling.Status item : items )
{
final String imageKey = ( item.severity() == org.eclipse.sapphire.modeling.Status.Severity.ERROR ? "error" : "warning" );
buffer.append( "<li style=\"image\" value=\"" + imageKey + "\">" + escapeForXml( item.message() ) + "</li>" );
i++;
if( count > 10 && i == 9 )
{
break;
}
}
if( count > 10 )
{
final String msg = problemsOverflowMessage.format( numberToString( count - 9 ) );
final String imageKey = ( validation.severity() == org.eclipse.sapphire.modeling.Status.Severity.ERROR ? "error" : "warning" );
buffer.append( "<br/><li style=\"image\" value=\"" + imageKey + "\">" + msg + "</li>" );
}
buffer.append( "</form>" );
text.setText( buffer.toString(), true, false );
text.setImage( "error", ImageData.readFromClassLoader( SwtResourceCache.class, "Error.png" ).required() );
text.setImage( "warning", ImageData.readFromClassLoader( SwtResourceCache.class, "Warning.png" ).required() );
}
private String numberToString( final int number )
{
switch( number )
{
case 2 : return two.text();
case 3 : return three.text();
case 4 : return four.text();
case 5 : return five.text();
case 6 : return six.text();
case 7 : return seven.text();
case 8 : return eight.text();
case 9 : return nine.text();
default : return String.valueOf( number );
}
}
private List<org.eclipse.sapphire.modeling.Status> gather( final org.eclipse.sapphire.modeling.Status status )
{
final List<org.eclipse.sapphire.modeling.Status> items = new ArrayList<org.eclipse.sapphire.modeling.Status>();
gather( status, items );
return items;
}
private void gather( final org.eclipse.sapphire.modeling.Status status,
final List<org.eclipse.sapphire.modeling.Status> items )
{
if( status.children().isEmpty() )
{
items.add( status );
}
else
{
for( org.eclipse.sapphire.modeling.Status child : status.children() )
{
gather( child, items );
}
}
}
};
treeViewer.setContentProvider( contentProvider );
treeViewer.setLabelProvider( labelProvider );
treeViewer.setInput( new Object() );
final MutableReference<Boolean> ignoreSelectionChange = new MutableReference<Boolean>( false );
final MutableReference<Boolean> ignoreExpandedStateChange = new MutableReference<Boolean>( false );
final Listener contentTreeListener = new Listener()
{
@Override
public void handle( final org.eclipse.sapphire.Event event )
{
if( event instanceof MasterDetailsContentOutline.SelectionChangedEvent )
{
if( ignoreSelectionChange.get() == true )
{
return;
}
final MasterDetailsContentOutline.SelectionChangedEvent evt = (MasterDetailsContentOutline.SelectionChangedEvent) event;
final List<MasterDetailsContentNodePart> selection = evt.selection();
final IStructuredSelection sel;
if( selection.isEmpty() )
{
sel = StructuredSelection.EMPTY;
}
else
{
sel = new StructuredSelection( selection );
}
if( ! treeViewer.getSelection().equals( sel ) )
{
for( MasterDetailsContentNodePart node : selection )
{
treeViewer.reveal( node );
}
treeViewer.setSelection( sel );
}
}
else if( event instanceof MasterDetailsContentOutline.NodeExpandedStateChangedEvent )
{
if( ignoreExpandedStateChange.get() == true )
{
return;
}
final MasterDetailsContentOutline.NodeExpandedStateChangedEvent evt = (MasterDetailsContentOutline.NodeExpandedStateChangedEvent) event;
final MasterDetailsContentNodePart node = evt.node();
final boolean expandedState = node.isExpanded();
if( treeViewer.getExpandedState( node ) != expandedState )
{
treeViewer.setExpandedState( node, expandedState );
}
}
else if( event instanceof MasterDetailsContentOutline.FilterChangedEvent )
{
final MasterDetailsContentOutline.FilterChangedEvent evt = (MasterDetailsContentOutline.FilterChangedEvent) event;
filteredTree.changeFilterText( evt.filter() );
}
}
};
outline.attach( contentTreeListener );
treeViewer.addSelectionChangedListener
(
new ISelectionChangedListener()
{
public void selectionChanged( final SelectionChangedEvent event )
{
ignoreSelectionChange.set( true );
try
{
final IStructuredSelection selection = (IStructuredSelection) event.getSelection();
final List<MasterDetailsContentNodePart> nodes = new ArrayList<MasterDetailsContentNodePart>();
for( Iterator<?> itr = selection.iterator(); itr.hasNext(); )
{
nodes.add( (MasterDetailsContentNodePart) itr.next() );
}
outline.setSelectedNodes( nodes );
}
finally
{
ignoreSelectionChange.set( false );
}
}
}
);
treeViewer.addTreeListener
(
new ITreeViewerListener()
{
public void treeExpanded( final TreeExpansionEvent event )
{
ignoreExpandedStateChange.set( true );
try
{
final MasterDetailsContentNodePart node = (MasterDetailsContentNodePart) event.getElement();
node.setExpanded( true );
}
finally
{
ignoreExpandedStateChange.set( false );
}
}
public void treeCollapsed( final TreeExpansionEvent event )
{
ignoreExpandedStateChange.set( true );
try
{
final MasterDetailsContentNodePart node = (MasterDetailsContentNodePart) event.getElement();
node.setExpanded( false );
}
finally
{
ignoreExpandedStateChange.set( false );
}
}
}
);
final ContentOutlineActionSupport actionSupport = new ContentOutlineActionSupport( outline, tree );
treeViewer.setExpandedElements( outline.getExpandedNodes().toArray() );
contentTreeListener.handle( new MasterDetailsContentOutline.SelectionChangedEvent( outline.getSelectedNodes() ) );
filteredTree.changeFilterText( outline.getFilterText() );
final ElementsTransfer transfer = new ElementsTransfer( getModelElement().type().getModelElementClass().getClassLoader() );
final Transfer[] transfers = new Transfer[] { transfer };
final DragSource dragSource = new DragSource( tree, DND.DROP_COPY | DND.DROP_MOVE );
dragSource.setTransfer( transfers );
final List<Element> dragElements = new ArrayList<Element>();
dragSource.addDragListener
(
new DragSourceListener()
{
public void dragStart( final DragSourceEvent event )
{
final TreeItem[] selection = tree.getSelection();
final String filter = outline().getFilterText();
if( ( filter == null || filter.length() == 0 ) && draggable( selection ) )
{
event.doit = true;
for( TreeItem item : selection )
{
final MasterDetailsContentNodePart node = (MasterDetailsContentNodePart) item.getData();
dragElements.add( node.getModelElement() );
}
}
else
{
event.doit = false;
}
}
protected boolean draggable( final TreeItem[] selection )
{
if( selection.length > 0 )
{
for( TreeItem item : selection )
{
final MasterDetailsContentNodePart node = (MasterDetailsContentNodePart) item.getData();
if( ! draggable( node ) )
{
return false;
}
}
return true;
}
return false;
}
protected boolean draggable( final MasterDetailsContentNodePart node )
{
final Element element = node.getModelElement();
if( element.parent() instanceof ElementList && node.controls( element ) )
{
return true;
}
return false;
}
public void dragSetData( final DragSourceEvent event )
{
event.data = dragElements;
}
public void dragFinished( final DragSourceEvent event )
{
if( event.detail == DND.DROP_MOVE )
{
// When drop target is the same editor as drag source, the drop handler takes care of removing
// elements from their original location. The following block of code accounts for the case when
// dropping into another editor.
boolean droppedIntoAnotherEditor = false;
for( Element dragElement : dragElements )
{
if( ! dragElement.disposed() )
{
droppedIntoAnotherEditor = true;
break;
}
}
if( droppedIntoAnotherEditor )
{
final TreeItem[] selection = tree.getSelection();
final List<MasterDetailsContentNodePart> dragNodes = new ArrayList<MasterDetailsContentNodePart>();
for( TreeItem item : selection )
{
dragNodes.add( (MasterDetailsContentNodePart) item.getData() );
}
final MasterDetailsContentNodePart parentNode = dragNodes.get( 0 ).getParentNode();
MasterDetailsContentNodePart selectionPostDelete = findSelectionPostDelete( parentNode.nodes().visible(), dragNodes );
if( selectionPostDelete == null )
{
selectionPostDelete = parentNode;
}
final Disposable suspension = outline.listeners().queue().suspend( SelectionChangedEventFilter.INSTANCE );
try
{
for( Element dragElement : dragElements )
{
final ElementList<?> dragElementContainer = (ElementList<?>) dragElement.parent();
dragElementContainer.remove( dragElement );
}
}
catch( Exception e )
{
// Log this exception unless the cause is EditFailedException. These exception
// are the result of the user declining a particular action that is necessary
// before the edit can happen (such as making a file writable).
final EditFailedException editFailedException = EditFailedException.findAsCause( e );
if( editFailedException == null )
{
Sapphire.service( LoggingService.class ).log( e );
}
}
finally
{
suspension.dispose();
outline.listeners().queue().process();
}
parentNode.getContentTree().setSelectedNode( selectionPostDelete );
}
}
dragElements.clear();
}
}
);
final DropTarget target = new DropTarget( tree, DND.DROP_COPY | DND.DROP_MOVE );
target.setTransfer( transfers );
target.addDropListener
(
new DropTargetAdapter()
{
public void dragOver( final DropTargetEvent event )
{
if( event.item != null )
{
final TreeItem dragOverItem = (TreeItem) event.item;
final MasterDetailsContentNodePart dragOverNode = (MasterDetailsContentNodePart) dragOverItem.getData();
final MasterDetailsContentNodePart parentNode = dragOverNode.getParentNode();
final List<MasterDetailsContentNodePart> siblingNodes = parentNode.nodes().visible();
final Point pt = dragOverItem.getDisplay().map( null, tree, event.x, event.y );
final Rectangle bounds = dragOverItem.getBounds();
boolean dragOverNodeAcceptedDrop = false;
if( pt.y > bounds.y + bounds.height / 3 && pt.y < bounds.y + bounds.height - bounds.height / 3 )
{
for( final PropertyDef dragOverTargetChildProperty : dragOverNode.getChildNodeFactoryProperties() )
{
if( dragOverTargetChildProperty instanceof ListProperty && ! dragOverTargetChildProperty.isReadOnly() )
{
dragOverNodeAcceptedDrop = true;
event.feedback = DND.FEEDBACK_SELECT;
break;
}
}
}
if( ! dragOverNodeAcceptedDrop )
{
MasterDetailsContentNodePart precedingNode = null;
MasterDetailsContentNodePart trailingNode = null;
if( pt.y < bounds.y + bounds.height / 2 )
{
precedingNode = findPrecedingItem( siblingNodes, dragOverNode );
trailingNode = dragOverNode;
event.feedback = DND.FEEDBACK_INSERT_BEFORE;
}
else
{
precedingNode = dragOverNode;
trailingNode = findTrailingItem( siblingNodes, dragOverNode );
event.feedback = DND.FEEDBACK_INSERT_AFTER;
}
boolean ok = false;
if( precedingNode != null )
{
final Element precedingElement = precedingNode.getModelElement();
if( precedingElement.parent() instanceof ElementList && precedingNode.controls( precedingElement ) )
{
ok = true;
}
}
if( ! ok && trailingNode != null )
{
final Element trailingElement = trailingNode.getModelElement();
if( trailingElement.parent() instanceof ElementList && trailingNode.controls( trailingElement ) )
{
ok = true;
}
}
if( ! ok )
{
event.feedback = DND.FEEDBACK_NONE;
}
}
}
event.feedback |= DND.FEEDBACK_SCROLL;
}
@SuppressWarnings( "unchecked" )
public void drop( final DropTargetEvent event )
{
if( event.data == null || event.item == null)
{
event.detail = DND.DROP_NONE;
return;
}
// Determine where something was dropped.
final List<ElementData> droppedElements = (List<ElementData>) event.data;
final TreeItem dropTargetItem = (TreeItem) event.item;
final MasterDetailsContentNodePart dropTargetNode = (MasterDetailsContentNodePart) dropTargetItem.getData();
final MasterDetailsContentNodePart parentNode = dropTargetNode.getParentNode();
final List<MasterDetailsContentNodePart> siblingNodes = parentNode.nodes().visible();
final Point pt = tree.getDisplay().map( null, tree, event.x, event.y );
final Rectangle bounds = dropTargetItem.getBounds();
MasterDetailsContentNodePart precedingNode = null;
MasterDetailsContentNodePart trailingNode = null;
boolean dropTargetNodeAcceptedDrop = false;
if( pt.y > bounds.y + bounds.height / 3 && pt.y < bounds.y + bounds.height - bounds.height / 3 )
{
for( final PropertyDef dropTargetChildProperty : dropTargetNode.getChildNodeFactoryProperties() )
{
if( dropTargetChildProperty instanceof ListProperty && ! dropTargetChildProperty.isReadOnly() )
{
dropTargetNodeAcceptedDrop = true;
break;
}
}
}
if( ! dropTargetNodeAcceptedDrop )
{
if( pt.y < bounds.y + bounds.height / 2 )
{
precedingNode = findPrecedingItem( siblingNodes, dropTargetNode );
trailingNode = dropTargetNode;
}
else
{
precedingNode = dropTargetNode;
trailingNode = findTrailingItem( siblingNodes, dropTargetNode );
}
}
// Determine whether the drop was valid from model standpoint and figure out
// where in the model the dropped elements are to be inserted.
ElementList<?> list = null;
int position = -1;
if( precedingNode != null )
{
final Element precedingElement = precedingNode.getModelElement();
if( precedingElement.parent() instanceof ElementList && ! precedingElement.parent().definition().isReadOnly() &&
precedingNode.controls( precedingElement ) )
{
list = (ElementList<?>) precedingElement.parent();
final Set<ElementType> possibleListElementTypes = list.definition().service( PossibleTypesService.class ).types();
for( final ElementData droppedElement : droppedElements )
{
if( ! possibleListElementTypes.contains( droppedElement.type() ) )
{
list = null;
break;
}
}
if( list != null )
{
position = list.indexOf( precedingElement ) + 1;
}
}
}
if( list == null && trailingNode != null )
{
final Element trailingElement = trailingNode.getModelElement();
if( trailingElement.parent() instanceof ElementList && ! trailingElement.parent().definition().isReadOnly() &&
trailingNode.controls( trailingElement ) )
{
list = (ElementList<?>) trailingElement.parent();
final Set<ElementType> possibleListElementTypes = list.definition().service( PossibleTypesService.class ).types();