/******************************************************************************
* Copyright (c) 2014 Oracle and Liferay
* 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
* Gregory Amerson - [363765] Page book control property should handle model paths
* Gregory Amerson - [377329] SapphireListControlledPageBook fails to render with model path for control property
******************************************************************************/
package org.eclipse.sapphire.ui.forms;
import java.util.List;
import java.util.Set;
import org.eclipse.sapphire.DisposeEvent;
import org.eclipse.sapphire.Element;
import org.eclipse.sapphire.Event;
import org.eclipse.sapphire.FilteredListener;
import org.eclipse.sapphire.ListProperty;
import org.eclipse.sapphire.Listener;
import org.eclipse.sapphire.LocalizableText;
import org.eclipse.sapphire.PropertyDef;
import org.eclipse.sapphire.Text;
import org.eclipse.sapphire.modeling.ModelPath;
import org.eclipse.sapphire.modeling.el.AndFunction;
import org.eclipse.sapphire.modeling.el.Function;
import org.eclipse.sapphire.modeling.el.FunctionContext;
import org.eclipse.sapphire.modeling.el.FunctionResult;
import org.eclipse.sapphire.ui.ISapphirePart;
import org.eclipse.sapphire.ui.ListSelectionService;
import org.eclipse.sapphire.ui.PartVisibilityEvent;
import org.eclipse.sapphire.ui.SapphirePart;
import org.eclipse.sapphire.util.IdentityHashSet;
import org.eclipse.sapphire.util.MutableReference;
import org.eclipse.swt.widgets.Display;
/**
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
* @author <a href="mailto:gregory.amerson@liferay.com">Greg Amerson</a>
*/
public final class DetailSectionPart extends PageBookPart
{
@Text( "Page book's property reference path \"{0}\" is invalid." )
private static LocalizableText invalidPath;
static
{
LocalizableText.init( DetailSectionPart.class );
}
private Element element;
private ListProperty property;
private PropertyEditorPart listPropertyEditorPart;
@Override
protected void init()
{
super.init();
final String pathString = ( (DetailSectionDef) this.definition ).getProperty().content();
final String pathStringSubstituted = substituteParams( pathString, this.params );
final ModelPath path = new ModelPath( pathStringSubstituted );
this.element = getLocalModelElement();
for( int i = 0, n = path.length(); i < n; i++ )
{
final ModelPath.Segment segment = path.segment( i );
if( segment instanceof ModelPath.ModelRootSegment )
{
this.element = this.element.root();
}
else if( segment instanceof ModelPath.ParentElementSegment )
{
this.element = this.element.parent().element();
}
else if( segment instanceof ModelPath.PropertySegment )
{
this.property = (ListProperty) resolve( this.element, ( (ModelPath.PropertySegment) segment ).getPropertyName() );
if ( i + 1 != n )
{
throw new RuntimeException( invalidPath.format( pathString ) );
}
}
else
{
throw new RuntimeException( invalidPath.format( pathString ) );
}
}
ISapphirePart ancestor = parent();
SapphirePart highestUninitializedAncestor = null;
while( ancestor instanceof SapphirePart && ! ( (SapphirePart) ancestor ).initialized() )
{
highestUninitializedAncestor = (SapphirePart) ancestor;
ancestor = ancestor.parent();
}
if( highestUninitializedAncestor == null )
{
initListSelectionServiceListener();
}
else
{
highestUninitializedAncestor.attach
(
new FilteredListener<PartInitializationEvent>()
{
@Override
protected void handleTypedEvent( final PartInitializationEvent event )
{
event.part().detach( this );
initListSelectionServiceListener();
}
}
);
}
}
@Override
protected Function initVisibleWhenFunction()
{
final Function function = new Function()
{
@Override
public String name()
{
return "VisibleIfListEditorVisible";
}
@Override
public FunctionResult evaluate( final FunctionContext context )
{
return new FunctionResult( this, context )
{
private Listener listPropertyEditorListener;
@Override
protected void init()
{
this.listPropertyEditorListener = new FilteredListener<PartVisibilityEvent>()
{
@Override
protected void handleTypedEvent( final PartVisibilityEvent event )
{
refresh();
}
};
DetailSectionPart.this.listPropertyEditorPart.attach( this.listPropertyEditorListener );
}
@Override
protected Object evaluate()
{
return DetailSectionPart.this.listPropertyEditorPart.visible();
}
@Override
public void dispose()
{
DetailSectionPart.this.listPropertyEditorPart.detach( this.listPropertyEditorListener );
this.listPropertyEditorListener = null;
super.dispose();
}
};
}
};
function.init();
final Function base = super.initVisibleWhenFunction();
if( base == null )
{
return function;
}
else
{
return AndFunction.create( base, function );
}
}
private void initListSelectionServiceListener()
{
this.listPropertyEditorPart = findPropertyEditor( this, this.element, this.property );
if( this.listPropertyEditorPart == null )
{
final String msg = "DetailsSectionPart did not find " + this.property;
throw new RuntimeException( msg );
}
final ListSelectionService listSelectionService = this.listPropertyEditorPart.service( ListSelectionService.class );
final MutableReference<Element> selectedElementRef = new MutableReference<Element>();
final Listener listSelectionServiceListener = new Listener()
{
@Override
public void handle( final Event event )
{
final List<Element> selectedElements = listSelectionService.selection();
final Element selectedElement = ( selectedElements.isEmpty() ? null : selectedElements.get( 0 ) );
if( selectedElementRef.get() != selectedElement )
{
selectedElementRef.set( selectedElement );
final Runnable inputChangeOperation = new Runnable()
{
public void run()
{
changePage( selectedElement );
}
};
Display.getDefault().syncExec( inputChangeOperation );
}
}
};
listSelectionService.attach( listSelectionServiceListener );
attach
(
new Listener()
{
@Override
public void handle( final Event event )
{
if( event instanceof DisposeEvent )
{
listSelectionService.detach( listSelectionServiceListener );
}
}
}
);
changePage( null );
}
private PropertyEditorPart findPropertyEditor( final ISapphirePart part,
final Element element,
final PropertyDef property )
{
return findPropertyEditor( part, element, property, new IdentityHashSet<ISapphirePart>() );
}
private PropertyEditorPart findPropertyEditor( final ISapphirePart part,
final Element element,
final PropertyDef property,
final Set<ISapphirePart> searchedParts )
{
if( searchedParts.contains( part ) )
{
return null;
}
if( part instanceof PropertyEditorPart )
{
final PropertyEditorPart propertyEditorPart = (PropertyEditorPart) part;
if( propertyEditorPart.getLocalModelElement() == element && propertyEditorPart.property().definition() == property )
{
return propertyEditorPart;
}
}
searchedParts.add( part );
if( part instanceof ContainerPart )
{
final ContainerPart<?> container = (ContainerPart<?>) part;
for( SapphirePart child : container.children().all() )
{
final PropertyEditorPart propertyEditorPart = findPropertyEditor( child, element, property, searchedParts );
if( propertyEditorPart != null )
{
return propertyEditorPart;
}
}
}
final ISapphirePart parent = part.parent();
if( parent != null )
{
return findPropertyEditor( parent, element, property, searchedParts );
}
return null;
}
}