/******************************************************************************
* Copyright (c) 2014 Oracle
* 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:
* Konstantin Komissarchik - initial implementation and ongoing maintenance
* Ling Hao - [329114] rewrite context help binding feature
******************************************************************************/
package org.eclipse.sapphire.ui.forms.swt;
import static org.eclipse.sapphire.modeling.util.MiscUtil.escapeForXml;
import static org.eclipse.sapphire.ui.SapphireActionSystem.ACTION_OUTLINE_HIDE;
import static org.eclipse.sapphire.ui.SapphireActionSystem.CONTEXT_EDITOR_PAGE;
import static org.eclipse.sapphire.ui.SapphireActionSystem.CONTEXT_EDITOR_PAGE_OUTLINE;
import static org.eclipse.sapphire.ui.SapphireActionSystem.CONTEXT_EDITOR_PAGE_OUTLINE_HEADER;
import static org.eclipse.sapphire.ui.SapphireActionSystem.CONTEXT_EDITOR_PAGE_OUTLINE_NODE;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.gd;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.gdfill;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.gdhfill;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.gdhhint;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.gdwhint;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.glayout;
import static org.eclipse.sapphire.ui.util.MiscUtil.findSelectionPostDelete;
import static org.eclipse.sapphire.util.CollectionsUtil.findPrecedingItem;
import static org.eclipse.sapphire.util.CollectionsUtil.findTrailingItem;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.StyledString.Styler;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.sapphire.Disposable;
import org.eclipse.sapphire.Element;
import org.eclipse.sapphire.ElementData;
import org.eclipse.sapphire.ElementList;
import org.eclipse.sapphire.ElementType;
import org.eclipse.sapphire.EventDeliveryJob;
import org.eclipse.sapphire.Filter;
import org.eclipse.sapphire.FilteredListener;
import org.eclipse.sapphire.ImageData;
import org.eclipse.sapphire.ListProperty;
import org.eclipse.sapphire.Listener;
import org.eclipse.sapphire.LocalizableText;
import org.eclipse.sapphire.LoggingService;
import org.eclipse.sapphire.Property;
import org.eclipse.sapphire.PropertyDef;
import org.eclipse.sapphire.Sapphire;
import org.eclipse.sapphire.Text;
import org.eclipse.sapphire.modeling.CapitalizationType;
import org.eclipse.sapphire.modeling.EditFailedException;
import org.eclipse.sapphire.modeling.localization.LabelTransformer;
import org.eclipse.sapphire.services.PossibleTypesService;
import org.eclipse.sapphire.ui.ISapphireEditorActionContributor;
import org.eclipse.sapphire.ui.PartVisibilityEvent;
import org.eclipse.sapphire.ui.Presentation;
import org.eclipse.sapphire.ui.SapphireAction;
import org.eclipse.sapphire.ui.SapphireActionGroup;
import org.eclipse.sapphire.ui.SapphireActionHandler;
import org.eclipse.sapphire.ui.SapphireEditor;
import org.eclipse.sapphire.ui.SapphireEditorPagePart;
import org.eclipse.sapphire.ui.SapphirePart;
import org.eclipse.sapphire.ui.SapphirePart.ImageChangedEvent;
import org.eclipse.sapphire.ui.SapphirePart.LabelChangedEvent;
import org.eclipse.sapphire.ui.SapphirePart.PartEvent;
import org.eclipse.sapphire.ui.def.DefinitionLoader;
import org.eclipse.sapphire.ui.def.EditorPageDef;
import org.eclipse.sapphire.ui.def.ISapphireDocumentation;
import org.eclipse.sapphire.ui.def.ISapphireDocumentationDef;
import org.eclipse.sapphire.ui.def.ISapphireDocumentationRef;
import org.eclipse.sapphire.ui.forms.MasterDetailsContentNodeList;
import org.eclipse.sapphire.ui.forms.MasterDetailsContentNodePart;
import org.eclipse.sapphire.ui.forms.MasterDetailsContentNodePart.DecorationEvent;
import org.eclipse.sapphire.ui.forms.MasterDetailsContentNodePart.NodeListEvent;
import org.eclipse.sapphire.ui.forms.MasterDetailsContentOutline;
import org.eclipse.sapphire.ui.forms.MasterDetailsEditorPageDef;
import org.eclipse.sapphire.ui.forms.MasterDetailsEditorPagePart;
import org.eclipse.sapphire.ui.forms.MasterDetailsEditorPagePart.OutlineHeaderTextEvent;
import org.eclipse.sapphire.ui.forms.SectionPart;
import org.eclipse.sapphire.ui.forms.TextDecoration;
import org.eclipse.sapphire.ui.forms.swt.internal.ElementsTransfer;
import org.eclipse.sapphire.ui.forms.swt.internal.SapphireToolTip;
import org.eclipse.sapphire.ui.forms.swt.internal.SectionPresentation;
import org.eclipse.sapphire.ui.forms.swt.internal.text.SapphireFormText;
import org.eclipse.sapphire.util.ListFactory;
import org.eclipse.sapphire.util.MutableReference;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
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.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.TextStyle;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.ui.forms.DetailsPart;
import org.eclipse.ui.forms.FormColors;
import org.eclipse.ui.forms.IDetailsPage;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.IFormPart;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.MasterDetailsBlock;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.Form;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.part.Page;
import org.eclipse.ui.progress.WorkbenchJob;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
/**
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
*/
public final class MasterDetailsEditorPage extends SapphireEditorFormPage implements ISapphireEditorActionContributor
{
@Text( "Additional {0} problems not shown..." )
private static LocalizableText problemsOverflowMessage;
@Text( "two" )
private static LocalizableText two;
@Text( "three" )
private static LocalizableText three;
@Text( "four" )
private static LocalizableText four;
@Text( "five" )
private static LocalizableText five;
@Text( "six" )
private static LocalizableText six;
@Text( "seven" )
private static LocalizableText seven;
@Text( "eight" )
private static LocalizableText eight;
@Text( "nine" )
private static LocalizableText nine;
static
{
LocalizableText.init( MasterDetailsEditorPage.class );
}
private SwtPresentation presentation;
private RootSection mainSection;
private ContentOutline contentOutlinePage;
private IPartListener2 partListener;
public MasterDetailsEditorPage( final SapphireEditor editor,
final Element element,
final DefinitionLoader.Reference<EditorPageDef> definition )
{
this( editor, element, definition, null );
}
public MasterDetailsEditorPage( final SapphireEditor editor,
final Element element,
final DefinitionLoader.Reference<EditorPageDef> definition,
final String pageName )
{
super( editor, element, definition );
final MasterDetailsEditorPagePart part = getPart();
this.presentation = new SwtPresentation( part, null, editor.getSite().getShell() )
{
@Override
public void render()
{
throw new UnsupportedOperationException();
}
};
String partName = pageName;
if( partName == null )
{
partName = part.definition().getPageName().localized( CapitalizationType.TITLE_STYLE, false );
}
setPartName( partName );
// Content Outline
final SapphireAction outlineHideAction = getPart().getActions( CONTEXT_EDITOR_PAGE ).getAction( ACTION_OUTLINE_HIDE );
final SapphireActionHandler outlineHideActionHandler = new SapphireActionHandler()
{
@Override
protected Object run( final Presentation context )
{
setDetailsMaximized( ! isDetailsMaximized() );
return null;
}
};
outlineHideActionHandler.init( outlineHideAction, null );
outlineHideActionHandler.setChecked( isDetailsMaximized() );
outlineHideAction.addHandler( outlineHideActionHandler );
}
@Override
public MasterDetailsEditorPagePart getPart()
{
return (MasterDetailsEditorPagePart) super.getPart();
}
public MasterDetailsEditorPageDef getDefinition()
{
return getPart().definition();
}
@Override
public String getId()
{
return getPartName();
}
public MasterDetailsContentOutline outline()
{
return getPart().outline();
}
public IDetailsPage getCurrentDetailsPage()
{
return this.mainSection.getCurrentDetailsSection();
}
@Override
protected void createFormContent( final IManagedForm managedForm )
{
final SapphireEditorPagePart part = getPart();
final ScrolledForm form = managedForm.getForm();
try
{
FormToolkit toolkit = managedForm.getToolkit();
toolkit.decorateFormHeading(managedForm.getForm().getForm());
this.mainSection = new RootSection();
this.mainSection.createContent( managedForm );
final ISapphireDocumentation doc = getDefinition().getDocumentation().content();
if( doc != null )
{
ISapphireDocumentationDef docdef = null;
if( doc instanceof ISapphireDocumentationDef )
{
docdef = (ISapphireDocumentationDef) doc;
}
else
{
docdef = ( (ISapphireDocumentationRef) doc ).resolve();
}
if( docdef != null )
{
HelpSystem.setHelp( managedForm.getForm().getBody(), docdef );
}
}
final SapphireActionGroup actions = part.getActions( CONTEXT_EDITOR_PAGE );
final SapphireActionPresentationManager actionPresentationManager = new SapphireActionPresentationManager( this.presentation, actions );
final SapphireToolBarManagerActionPresentation toolbarActionPresentation = new SapphireToolBarManagerActionPresentation( actionPresentationManager );
final IToolBarManager toolbarManager = form.getToolBarManager();
toolbarActionPresentation.setToolBarManager( toolbarManager );
toolbarActionPresentation.render();
final SapphireKeyboardActionPresentation keyboardActionPresentation = new SapphireKeyboardActionPresentation( actionPresentationManager );
keyboardActionPresentation.attach( toolbarActionPresentation.getToolBar() );
keyboardActionPresentation.render();
part.attach
(
new FilteredListener<MasterDetailsEditorPagePart.DetailsFocusRequested>()
{
@Override
protected void handleTypedEvent( final MasterDetailsEditorPagePart.DetailsFocusRequested event )
{
setFocusOnDetails();
}
}
);
this.partListener = new IPartListener2()
{
public void partActivated( final IWorkbenchPartReference partRef )
{
}
public void partBroughtToTop( final IWorkbenchPartReference partRef )
{
}
public void partClosed( final IWorkbenchPartReference partRef )
{
if( ! isDetailsMaximized() )
{
getPart().state().getContentOutlineState().setRatio( MasterDetailsEditorPage.this.mainSection.getOutlineRatio() );
}
}
public void partDeactivated( final IWorkbenchPartReference partRef )
{
}
public void partOpened( final IWorkbenchPartReference partRef )
{
}
public void partHidden( final IWorkbenchPartReference partRef )
{
}
public void partVisible( final IWorkbenchPartReference partRef )
{
}
public void partInputChanged( final IWorkbenchPartReference partRef )
{
}
};
getSite().getPage().addPartListener( this.partListener );
}
catch( final Exception e )
{
if( this.mainSection != null )
{
this.mainSection.dispose();
this.mainSection = null;
final Composite body = (Composite) ( (Form) form.getChildren()[ 0 ] ).getChildren()[ 1 ];
for( Control control : body.getChildren() )
{
control.dispose();
}
final Color bgcolor = body.getDisplay().getSystemColor( SWT.COLOR_WHITE );
final Composite composite = new Composite( body, SWT.NONE );
composite.setLayoutData( gdfill() );
composite.setLayout( glayout( 1, 5, 5, 10, 5 ) );
composite.setBackground( bgcolor );
final Composite msgAndShowStackTraceLinkComposite = new Composite( composite, SWT.NONE );
msgAndShowStackTraceLinkComposite.setLayoutData( gdhfill() );
msgAndShowStackTraceLinkComposite.setLayout( glayout( 2, 0, 0 ) );
msgAndShowStackTraceLinkComposite.setBackground( bgcolor );
String message = e.getMessage();
if( message == null )
{
message = e.getClass().getName();
}
else
{
message = message.replace( "&", "&" );
message = message.replace( "<", "<" );
}
final SapphireFormText text = new SapphireFormText( msgAndShowStackTraceLinkComposite, SWT.NONE );
text.setLayoutData( gdhfill() );
text.setText( "<form><li style=\"image\" value=\"error\">" + message + "</li></form>", true, false );
text.setImage( "error", ImageData.readFromClassLoader( SwtResourceCache.class, "Error.png" ).required() );
text.setBackground( bgcolor );
final SapphireFormText showStackTraceLink = new SapphireFormText( msgAndShowStackTraceLinkComposite, SWT.NONE );
showStackTraceLink.setLayoutData( gd() );
showStackTraceLink.setText( "<form><p><a href=\"show-stack\">Show stack trace...</a></p></form>", true, false );
showStackTraceLink.setBackground( bgcolor );
showStackTraceLink.addHyperlinkListener
(
new HyperlinkAdapter()
{
@Override
public void linkActivated( final HyperlinkEvent event )
{
showStackTraceLink.setVisible( false );
final Label separator = new Label( composite, SWT.SEPARATOR | SWT.HORIZONTAL );
separator.setLayoutData( gdhfill() );
separator.setBackground( bgcolor );
final org.eclipse.swt.widgets.Text stack
= new org.eclipse.swt.widgets.Text( composite, SWT.MULTI | SWT.READ_ONLY | SWT.BORDER | SWT.V_SCROLL );
stack.setLayoutData( gdfill() );
stack.setBackground( bgcolor );
final StringWriter w = new StringWriter();
e.printStackTrace( new PrintWriter( w ) );
stack.setText( w.getBuffer().toString() );
body.layout( true, true );
}
}
);
}
}
}
public IContentOutlinePage getContentOutlinePage()
{
if( this.contentOutlinePage == null )
{
this.contentOutlinePage = new ContentOutline();
}
return this.contentOutlinePage;
}
public boolean isDetailsMaximized()
{
return ! getPart().state().getContentOutlineState().getVisible().content();
}
public void setDetailsMaximized( final boolean maximized )
{
this.mainSection.setDetailsMaximized( maximized );
getPart().state().getContentOutlineState().setVisible( ! maximized );
}
public double getOutlineRatio()
{
double contentOutlineRatio = getPart().state().getContentOutlineState().getRatio().content();
if( contentOutlineRatio < 0 || contentOutlineRatio > 1 )
{
contentOutlineRatio = 0.3d;
}
return contentOutlineRatio;
}
public void setOutlineRatio( final Double ratio )
{
if( ratio < 0 || ratio > 1 )
{
throw new IllegalArgumentException();
}
this.mainSection.setOutlineRatio( ratio );
getPart().state().getContentOutlineState().setRatio( ratio );
}
@Override
public void setActive( final boolean active )
{
if( this.mainSection != null )
{
super.setActive( active );
}
}
@Override
public boolean isDirty()
{
return false;
}
@Override
public void setFocus()
{
if( isDetailsMaximized() )
{
setFocusOnDetails();
}
else
{
setFocusOnContentOutline();
}
}
public void setFocusOnContentOutline()
{
if( isDetailsMaximized() )
{
setDetailsMaximized( false );
}
if( this.mainSection != null )
{
this.mainSection.masterSection.tree.setFocus();
}
}
public void setFocusOnDetails()
{
final Control control = findFirstFocusableControl( this.mainSection.detailsSectionControl );
if( control != null )
{
control.setFocus();
}
}
private Control findFirstFocusableControl( final Control control )
{
if( control instanceof Combo || control instanceof Link ||
control instanceof List<?> || control instanceof Table || control instanceof Tree )
{
return control;
}
else if( control instanceof org.eclipse.swt.widgets.Text )
{
if( ( ( (org.eclipse.swt.widgets.Text) control ).getStyle() & SWT.READ_ONLY ) == 0 )
{
return control;
}
}
else if( control instanceof Button )
{
final Button button = (Button) control;
final int style = button.getStyle();
if( ( style & SWT.CHECK ) != 0 || ( ( style & SWT.RADIO ) != 0 && button.getSelection() == true ) )
{
return control;
}
}
else if( control instanceof Composite )
{
for( Control child : ( (Composite) control ).getChildren() )
{
final Control res = findFirstFocusableControl( child );
if( res != null )
{
return res;
}
}
}
return null;
}
private FilteredTree createContentOutline( final Composite parent,
final MasterDetailsContentOutline outline,
final boolean addBorders )
{
final int treeStyle = ( addBorders ? SWT.BORDER : SWT.NONE ) | SWT.MULTI;
final ContentOutlineFilteredTree filteredTree = new ContentOutlineFilteredTree( parent, treeStyle, outline );
final TreeViewer treeViewer = filteredTree.getViewer();
final Tree tree = treeViewer.getTree();
final ITreeContentProvider contentProvider = new ITreeContentProvider()
{
private final Listener listener = new Listener()
{
@Override
public void handle( final org.eclipse.sapphire.Event event )
{
if( event instanceof PartEvent )
{
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();
for( final ElementData droppedElement : droppedElements )
{
if( ! possibleListElementTypes.contains( droppedElement.type() ) )
{
list = null;
break;
}
}
if( list != null )
{
position = list.indexOf( trailingElement );
}
}
}
if( list == null )
{
for( PropertyDef dropTargetChildProperty : dropTargetNode.getChildNodeFactoryProperties() )
{
if( dropTargetChildProperty instanceof ListProperty && ! dropTargetChildProperty.isReadOnly() )
{
final ListProperty dropTargetChildListProperty = (ListProperty) dropTargetChildProperty;
boolean compatible = true;
final Set<ElementType> possibleListElementTypes = dropTargetChildListProperty.service( PossibleTypesService.class ).types();
for( final ElementData droppedElement : droppedElements )
{
if( ! possibleListElementTypes.contains( droppedElement.type() ) )
{
compatible = false;
break;
}
}
if( compatible )
{
list = dropTargetNode.getLocalModelElement().property( dropTargetChildListProperty );
position = list.size();
}
}
}
}
if( list == null )
{
event.detail = DND.DROP_NONE;
return;
}
// Prevent a drop within a drag element.
for( Property p = list; p != null; p = p.element().parent() )
{
for( final Element dragElement : dragElements )
{
if( p.element() == dragElement )
{
event.detail = DND.DROP_NONE;
return;
}
}
}
// Perform the removal and insertion into the new location.
final Disposable suspension = outline.listeners().queue().suspend( SelectionChangedEventFilter.INSTANCE );
try
{
if( event.detail == DND.DROP_MOVE )
{
for( Element dragElement : dragElements )
{
final ElementList<?> dragElementContainer = (ElementList<?>) dragElement.parent();
if( dragElementContainer == list && dragElementContainer.indexOf( dragElement ) < position )
{
position--;
}
dragElementContainer.remove( dragElement );
}
}
final List<MasterDetailsContentNodePart> newSelection = new ArrayList<MasterDetailsContentNodePart>();
for( final ElementData droppedElement : droppedElements )
{
final Element insertedElement = list.insert( droppedElement.type(), position );
insertedElement.copy( droppedElement );
newSelection.add( parentNode.findNode( insertedElement ) );
position++;
}
parentNode.getContentTree().setSelectedNodes( newSelection );
}
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 );
}
event.detail = DND.DROP_NONE;
}
finally
{
suspension.dispose();
outline.listeners().queue().process();
}
}
}
);
tree.addDisposeListener
(
new DisposeListener()
{
public void widgetDisposed( final DisposeEvent event )
{
outline.detach( contentTreeListener );
actionSupport.dispose();
}
}
);
return filteredTree;
}
private static void updateExpandedState( final MasterDetailsContentOutline contentTree,
final Tree tree )
{
final Set<MasterDetailsContentNodePart> expandedNodes = new HashSet<MasterDetailsContentNodePart>();
gatherExpandedNodes( tree.getItems(), expandedNodes );
contentTree.setExpandedNodes( expandedNodes );
}
private static void gatherExpandedNodes( final TreeItem[] items,
final Set<MasterDetailsContentNodePart> result )
{
for( TreeItem item : items )
{
if( item.getExpanded() == true )
{
result.add( (MasterDetailsContentNodePart) item.getData() );
gatherExpandedNodes( item.getItems(), result );
}
}
}
public void dispose()
{
super.dispose();
if( this.mainSection != null )
{
this.mainSection.dispose();
}
if( this.partListener != null )
{
getSite().getPage().removePartListener( this.partListener );
}
}
private static final class ContentOutlineFilteredTree extends FilteredTree
{
private final MasterDetailsContentOutline contentTree;
private WorkbenchJob refreshJob;
public ContentOutlineFilteredTree( final Composite parent,
final int treeStyle,
final MasterDetailsContentOutline contentTree )
{
super
(
parent,
treeStyle,
new PatternFilter()
{
@Override
protected boolean isLeafMatch( final Viewer viewer, final Object element )
{
return wordMatches( ( (MasterDetailsContentNodePart) element ).getLabel() );
}
},
true
);
this.contentTree = contentTree;
setBackground( Display.getCurrent().getSystemColor( SWT.COLOR_WHITE ) );
}
@Override
protected WorkbenchJob doCreateRefreshJob()
{
final WorkbenchJob base = super.doCreateRefreshJob();
this.refreshJob = new WorkbenchJob( base.getName() )
{
public IStatus runInUIThread( final IProgressMonitor monitor )
{
IStatus st = base.runInUIThread( new NullProgressMonitor() );
if( st.getSeverity() == IStatus.CANCEL )
{
return st;
}
ContentOutlineFilteredTree.this.contentTree.setFilterText( getFilterString() );
updateExpandedState( ContentOutlineFilteredTree.this.contentTree, getViewer().getTree() );
return Status.OK_STATUS;
}
};
return this.refreshJob;
}
public void changeFilterText( final String filterText )
{
final String currentFilterText = getFilterString();
if( currentFilterText != null && ! currentFilterText.equals( filterText ) )
{
setFilterText( filterText );
textChanged();
//this.refreshJob.cancel();
//this.refreshJob.runInUIThread( null );
}
}
}
private final class ContentOutline
extends Page
implements IContentOutlinePage
{
private Composite outerComposite = null;
private FilteredTree filteredTree = null;
private TreeViewer treeViewer = null;
private SapphireActionPresentationManager actionPresentationManager;
public void init( final IPageSite pageSite )
{
super.init( pageSite );
pageSite.setSelectionProvider( this );
}
@Override
public void createControl( final Composite parent )
{
this.outerComposite = new Composite( parent, SWT.NONE );
this.outerComposite.setLayout( glayout( 1, 5, 5 ) );
this.outerComposite.setBackground( Display.getCurrent().getSystemColor( SWT.COLOR_WHITE ) );
this.filteredTree = createContentOutline( this.outerComposite, outline(), false );
this.filteredTree.setLayoutData( gdfill() );
this.treeViewer = this.filteredTree.getViewer();
final SapphireEditorPagePart part = getPart();
final SapphireActionGroup actions = part.getActions( CONTEXT_EDITOR_PAGE_OUTLINE_HEADER );
this.actionPresentationManager = new SapphireActionPresentationManager( MasterDetailsEditorPage.this.presentation, actions );
final SapphireToolBarManagerActionPresentation toolbarActionsPresentation = new SapphireToolBarManagerActionPresentation( this.actionPresentationManager );
toolbarActionsPresentation.setToolBarManager( getSite().getActionBars().getToolBarManager() );
toolbarActionsPresentation.render();
final SapphireKeyboardActionPresentation keyboardActionsPresentation = new SapphireKeyboardActionPresentation( this.actionPresentationManager );
keyboardActionsPresentation.attach( this.filteredTree.getFilterControl() );
keyboardActionsPresentation.render();
}
@Override
public Control getControl()
{
return this.outerComposite;
}
@Override
public void setFocus()
{
this.treeViewer.getControl().setFocus();
}
public ISelection getSelection()
{
if( this.treeViewer == null )
{
return StructuredSelection.EMPTY;
}
return this.treeViewer.getSelection();
}
public void setSelection( final ISelection selection )
{
if( this.treeViewer != null )
{
this.treeViewer.setSelection( selection );
}
}
public void addSelectionChangedListener( final ISelectionChangedListener listener )
{
}
public void removeSelectionChangedListener( final ISelectionChangedListener listener )
{
}
@Override
public void dispose()
{
super.dispose();
this.actionPresentationManager.dispose();
this.actionPresentationManager = null;
}
}
private final class ContentOutlineActionSupport
{
private final MasterDetailsContentOutline contentTree;
private final Listener contentOutlineListener;
private final Tree tree;
private final Menu menu;
private SapphireActionPresentationManager actionPresentationManager;
private SapphireActionGroup tempActions;
private ContentOutlineActionSupport( final MasterDetailsContentOutline contentOutline,
final Tree tree )
{
this.contentTree = contentOutline;
this.tree = tree;
this.menu = new Menu( tree );
this.tree.setMenu( this.menu );
this.contentOutlineListener = new Listener()
{
@Override
public void handle( final org.eclipse.sapphire.Event event )
{
if( event instanceof MasterDetailsContentOutline.SelectionChangedEvent )
{
final MasterDetailsContentOutline.SelectionChangedEvent evt = (MasterDetailsContentOutline.SelectionChangedEvent) event;
handleSelectionChangedEvent( evt.selection() );
}
}
};
this.contentTree.attach( this.contentOutlineListener );
handleSelectionChangedEvent( contentOutline.getSelectedNodes() );
}
private void handleSelectionChangedEvent( final List<MasterDetailsContentNodePart> selection )
{
for( MenuItem item : this.menu.getItems() )
{
item.dispose();
}
if( this.tempActions != null )
{
this.tempActions.dispose();
this.tempActions = null;
}
if( this.actionPresentationManager != null )
{
this.actionPresentationManager.dispose();
this.actionPresentationManager = null;
}
final SapphireEditorPagePart part = getPart();
final SapphireActionGroup actions;
if( selection.size() == 1 )
{
final MasterDetailsContentNodePart node = selection.get( 0 );
actions = node.getActions( CONTEXT_EDITOR_PAGE_OUTLINE_NODE );
}
else
{
this.tempActions = new SapphireActionGroup( part, CONTEXT_EDITOR_PAGE_OUTLINE );
actions = this.tempActions;
}
this.actionPresentationManager
= new SapphireActionPresentationManager( MasterDetailsEditorPage.this.presentation, actions );
final SapphireMenuActionPresentation menuActionPresentation = new SapphireMenuActionPresentation( this.actionPresentationManager );
menuActionPresentation.setMenu( this.menu );
menuActionPresentation.render();
final SapphireKeyboardActionPresentation keyboardActionPresentation = new SapphireKeyboardActionPresentation( this.actionPresentationManager );
keyboardActionPresentation.attach( this.tree );
keyboardActionPresentation.render();
}
public void dispose()
{
this.contentTree.detach( this.contentOutlineListener );
if( this.tempActions != null )
{
this.tempActions.dispose();
this.tempActions = null;
}
if( this.actionPresentationManager != null )
{
this.actionPresentationManager.dispose();
this.actionPresentationManager = null;
}
}
}
public IAction getAction(String actionId)
{
// TODO return action handlers for the global actions such as Delete, Select All
return null;
}
private final class RootSection
extends MasterDetailsBlock
{
private MasterSection masterSection;
private List<IDetailsPage> detailsSections;
private Control detailsSectionControl;
public RootSection()
{
this.detailsSections = new ArrayList<IDetailsPage>();
this.detailsSectionControl = null;
}
@Override
public void createContent( final IManagedForm managedForm )
{
super.createContent( managedForm );
setOutlineRatio( MasterDetailsEditorPage.this.getOutlineRatio() );
try
{
final Field field = this.detailsPart.getClass().getDeclaredField( "pageBook" );
field.setAccessible( true );
this.detailsSectionControl = (Control) field.get( this.detailsPart );
}
catch( Exception e )
{
Sapphire.service( LoggingService.class ).log( e );
}
this.masterSection.handleSelectionChangedEvent( outline().getSelectedNodes() );
setDetailsMaximized( isDetailsMaximized() );
}
@Override
protected void createMasterPart( final IManagedForm managedForm,
final Composite parent )
{
this.masterSection = new MasterSection( managedForm, parent );
final org.eclipse.ui.forms.SectionPart spart = new org.eclipse.ui.forms.SectionPart(this.masterSection);
managedForm.addPart(spart);
}
@Override
protected void registerPages( final DetailsPart detailsPart )
{
final IDetailsPage detailsPage = new DetailsSection();
detailsPart.registerPage( MasterDetailsContentNodePart.class, detailsPage );
this.detailsSections.add( detailsPage );
}
@Override
protected void applyLayoutData( final SashForm sashForm )
{
sashForm.setLayoutData( gdwhint( gdhhint( gdfill(), 200 ), 400 ) );
}
public IDetailsPage getCurrentDetailsSection()
{
return this.detailsPart.getCurrentPage();
}
public void setDetailsMaximized( final boolean maximized )
{
this.sashForm.setMaximizedControl( maximized ? this.detailsSectionControl : null );
}
public double getOutlineRatio()
{
final Control[] children = this.sashForm.getChildren();
final int outline = children[ 0 ].getSize().x;
final int details = children[ 1 ].getSize().x;
final int total = outline + details;
final double ratio = ( (double) outline ) / ( (double) total );
return ratio;
}
public void setOutlineRatio( final double ratio )
{
final int total = Integer.MAX_VALUE;
final int outline = (int) ( total * ratio );
final int details = total - outline;
this.sashForm.setWeights( new int[] { outline, details } );
}
public void dispose()
{
if( this.masterSection != null )
{
this.masterSection.dispose();
}
for( IDetailsPage section : this.detailsSections )
{
section.dispose();
}
}
@Override
protected void createToolBarActions( IManagedForm managedForm )
{
}
}
private final class MasterSection extends Section
{
private IManagedForm managedForm;
private org.eclipse.ui.forms.SectionPart sectionPart;
private TreeViewer treeViewer;
private Tree tree;
private void refreshOutlineHeaderText()
{
setText( LabelTransformer.transform( getPart().getOutlineHeaderText(), CapitalizationType.TITLE_STYLE, false ) );
}
public MasterSection( final IManagedForm managedForm,
final Composite parent)
{
super( parent, Section.TITLE_BAR );
final FormToolkit toolkit = managedForm.getToolkit();
FormColors colors = toolkit.getColors();
this.setMenu(parent.getMenu());
toolkit.adapt(this, true, true);
if (this.toggle != null) {
this.toggle.setHoverDecorationColor(colors
.getColor(IFormColors.TB_TOGGLE_HOVER));
this.toggle.setDecorationColor(colors
.getColor(IFormColors.TB_TOGGLE));
}
this.setFont(createBoldFont(colors.getDisplay(), this.getFont()));
colors.initializeSectionToolBarColors();
this.setTitleBarBackground(colors.getColor(IFormColors.TB_BG));
this.setTitleBarBorderColor(colors
.getColor(IFormColors.TB_BORDER));
this.setTitleBarForeground(colors
.getColor(IFormColors.TB_TOGGLE));
this.marginWidth = 10;
this.marginHeight = 10;
setLayoutData( gdfill() );
setLayout( glayout( 1, 0, 0 ) );
final SapphireEditorPagePart part = getPart();
final Listener pagePartListener = new FilteredListener<OutlineHeaderTextEvent>()
{
@Override
protected void handleTypedEvent( final OutlineHeaderTextEvent event )
{
refreshOutlineHeaderText();
}
};
part.attach( pagePartListener );
refreshOutlineHeaderText();
final Composite client = toolkit.createComposite( this );
client.setLayout( glayout( 1, 0, 0 ) );
this.managedForm = managedForm;
final MasterDetailsContentOutline contentTree = outline();
final FilteredTree filteredTree = createContentOutline( client, contentTree, true );
this.treeViewer = filteredTree.getViewer();
this.tree = this.treeViewer.getTree();
this.sectionPart = new org.eclipse.ui.forms.SectionPart( this );
this.managedForm.addPart( this.sectionPart );
contentTree.attach
(
new FilteredListener<MasterDetailsContentOutline.SelectionChangedEvent>()
{
@Override
protected void handleTypedEvent( final MasterDetailsContentOutline.SelectionChangedEvent event )
{
handleSelectionChangedEvent( event.selection() );
}
}
);
final ToolBar toolbar = new ToolBar( this, SWT.FLAT | SWT.HORIZONTAL );
setTextClient( toolbar );
final SapphireActionGroup actions = part.getActions( CONTEXT_EDITOR_PAGE_OUTLINE_HEADER );
final SapphireActionPresentationManager actionPresentationManager
= new SapphireActionPresentationManager( MasterDetailsEditorPage.this.presentation, actions );
final SapphireToolBarActionPresentation toolbarActionsPresentation = new SapphireToolBarActionPresentation( actionPresentationManager );
toolbarActionsPresentation.setToolBar( toolbar );
toolbarActionsPresentation.render();
final SapphireKeyboardActionPresentation keyboardActionsPresentation = new SapphireKeyboardActionPresentation( actionPresentationManager );
keyboardActionsPresentation.attach( filteredTree.getFilterControl() );
keyboardActionsPresentation.render();
toolkit.paintBordersFor( this );
setClient( client );
this.tree.addDisposeListener
(
new DisposeListener()
{
public void widgetDisposed( final DisposeEvent event )
{
part.detach( pagePartListener );
}
}
);
}
private Font createBoldFont(Display display, Font regularFont) {
FontData[] fontDatas = regularFont.getFontData();
for (int i = 0; i < fontDatas.length; i++) {
fontDatas[i].setStyle(fontDatas[i].getStyle() | SWT.BOLD);
}
return new Font(display, fontDatas);
}
private void handleSelectionChangedEvent( final List<MasterDetailsContentNodePart> selection )
{
final IStructuredSelection sel
= ( selection.isEmpty() ? StructuredSelection.EMPTY : new StructuredSelection( selection.get( 0 ) ) );
this.managedForm.fireSelectionChanged( this.sectionPart, sel );
}
}
private class DetailsSection implements IDetailsPage
{
private MasterDetailsContentNodePart node;
private Composite composite;
private final Listener listener;
private List<SectionPart> sections;
private List<SectionPresentation> presentations;
public DetailsSection()
{
this.listener = new FilteredListener<PartVisibilityEvent>()
{
@Override
protected void handleTypedEvent( final PartVisibilityEvent event )
{
refreshSections();
}
};
this.sections = Collections.emptyList();
}
public void initialize( final IManagedForm form )
{
}
public final void createContents( final Composite parent )
{
this.composite = parent;
this.composite.setLayout( glayout( 2, 0, 0 ) );
this.composite.setBackground( getPart().getSwtResourceCache().color( org.eclipse.sapphire.Color.WHITE ) );
this.composite.setBackgroundMode( SWT.INHERIT_DEFAULT );
refreshSections();
}
public void commit(boolean onSave) {
}
public boolean isDirty() {
return false;
}
public boolean isStale() {
return false;
}
public void refresh()
{
}
public void setFocus() {
}
public boolean setFormInput(Object input) {
return false;
}
public void selectionChanged( final IFormPart part,
final ISelection selection )
{
final IStructuredSelection ssel = (IStructuredSelection) selection;
if( ssel.size() == 1 )
{
this.node = (MasterDetailsContentNodePart) ssel.getFirstElement();
}
else
{
this.node = null;
}
refreshSections();
}
private void refreshSections()
{
if( this.presentations != null )
{
for( final Presentation presentation : this.presentations )
{
presentation.dispose();
}
}
if( this.composite.getChildren().length > 0 )
{
throw new IllegalStateException();
}
for( final SectionPart section : this.sections )
{
section.detach( this.listener );
}
if( this.node != null )
{
this.sections = this.node.getSections();
}
else
{
this.sections = ListFactory.empty();
}
final ListFactory<SectionPresentation> presentationsListFactory = ListFactory.start();
for( final SectionPart section : this.sections )
{
section.attach( this.listener );
if( section.visible() )
{
final SectionPresentation presentation = (SectionPresentation) section.createPresentation( MasterDetailsEditorPage.this.presentation, this.composite );
presentationsListFactory.add( presentation );
presentation.render();
}
}
this.presentations = presentationsListFactory.result();
this.composite.getParent().layout( true, true );
}
public void dispose()
{
for( SectionPart section : this.sections )
{
section.detach( this.listener );
}
}
}
private static final class TreeViewerUpdateJob implements Runnable
{
private final TreeViewer tree;
private final Object element;
public TreeViewerUpdateJob( final TreeViewer tree,
final Object element )
{
this.tree = tree;
this.element = element;
}
public void run()
{
this.tree.update( this.element, null );
}
}
private static final class SelectionChangedEventFilter implements Filter<EventDeliveryJob>
{
public static SelectionChangedEventFilter INSTANCE = new SelectionChangedEventFilter();
@Override
public boolean allows( final EventDeliveryJob job )
{
return ! ( job.event() instanceof MasterDetailsContentOutline.SelectionChangedEvent );
}
}
}