package com.cedarsoft.spring.rcp.hierarchy;
import com.cedarsoft.spring.SpringSupport;
import com.cedarsoft.spring.rcp.PageComponentContextAware;
import com.cedarsoft.spring.rcp.events.DetailsTreeSelectionEvent;
import com.cedarsoft.lookup.Lookup;
import com.cedarsoft.utils.Renderer;
import com.cedarsoft.utils.TypeRegistry;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.richclient.application.Application;
import org.springframework.richclient.application.ApplicationServices;
import org.springframework.richclient.application.PageComponentContext;
import org.springframework.richclient.core.DefaultMessage;
import org.springframework.richclient.dialog.TitlePane;
import org.springframework.richclient.factory.ComponentFactory;
import org.springframework.richclient.form.Form;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import java.awt.BorderLayout;
import java.awt.Component;
/**
*
*/
public class HierarchyDetailsModel implements HierarchyTree.SelectionListener, ApplicationListener {
@NotNull
private final TypeRegistry<HierarchyLayer<?, ?>> hierarchyLayers;
private ApplicationListener delegatingApplicationListener;
/**
* Contains the top panel
*/
@NotNull
private final JPanel topPanel = new JPanel( new BorderLayout() );
/**
* The title pane that is used when *no* other content for the top panel is available
*/
@NotNull
private final TitlePane titlePane = new TitlePane();
/**
* Containst he content
*/
@NotNull
private final JPanel contentPanel = new JPanel( new BorderLayout() );
private Object currentlySelectedObject;
public HierarchyDetailsModel( @NotNull TypeRegistry<HierarchyLayer<?, ?>> hierarchyLayers ) {
this.hierarchyLayers = hierarchyLayers;
}
@Override
public void unselect( @NotNull DetailsTreeSelectionEvent event ) {
//noinspection AssignmentToNull
delegatingApplicationListener = null;
}
@Override
public void select( @NotNull DetailsTreeSelectionEvent event ) {
currentlySelectedObject = event.getObject();
//noinspection unchecked
HierarchyLayer<Object, ?> layer = ( HierarchyLayer<Object, ?> ) hierarchyLayers.getElement( currentlySelectedObject.getClass() );
//Clean up
contentPanel.removeAll();
topPanel.removeAll();
//noinspection AssignmentToNull
delegatingApplicationListener = null;
Renderer<Object, Object> renderer = getRenderer( layer );
//Now fill it
//noinspection unchecked
DetailsProvider<Object> detailsProvider = layer.getLookup().lookup( DetailsProvider.class );
if ( detailsProvider != null ) {
contentPanel.add( createDetailsComponent( detailsProvider, currentlySelectedObject ), BorderLayout.CENTER );
} else {
contentPanel.add( new JPanel(), BorderLayout.CENTER );
}
//noinspection unchecked
TitleProvider<Object> titleProvider = layer.getLookup().lookup( TitleProvider.class );
if ( titleProvider != null ) {
topPanel.add( createTitleComponent( titleProvider, currentlySelectedObject ) );
} else {
//Set the title based on the selected object
titlePane.setTitle( renderer.render( currentlySelectedObject, null ) );
titlePane.setMessage( new DefaultMessage( getMessage( currentlySelectedObject.getClass().getName() + ".message" ) ) );
topPanel.add( titlePane.getControl(), BorderLayout.CENTER );
}
updateContentPanelBorder();
contentPanel.validate();
contentPanel.repaint();
topPanel.validate();
topPanel.repaint();
}
@NotNull
protected JComponent createTitleComponent( @NotNull TitleProvider<Object> provider, @NotNull Object selectedObject ) {
Lookup details = provider.getTitle( selectedObject );
//Insert the component
JComponent component;
DetailsComponentFactory componentFactory = details.lookup( DetailsComponentFactory.class );
if ( componentFactory != null ) {
component = componentFactory.createComponent();
} else {
component = details.lookup( JComponent.class );
if ( component == null ) {
throw new IllegalStateException( "No componentFactory and no component provided for " + selectedObject );
}
}
component.setBorder( BorderFactory.createEmptyBorder( 5, 5, 5, 5 ) );
return component;
}
/**
* Returns the details component
*
* @param provider the details provider
* @param selectedObject the selected object
* @return the details component for the given provider and selected object
*/
@NotNull
private Component createDetailsComponent( @NotNull DetailsProvider<Object> provider, @NotNull Object selectedObject ) {
Lookup details = provider.getDetails( selectedObject );
//Delegate the view
PageComponentContextAware pageComponentContextAware = details.lookup( PageComponentContextAware.class );
if ( pageComponentContextAware != null ) {
pageComponentContextAware.updateContext( getContext() );
}
//When a form is available, set the title pane as messagable
Form form = details.lookup( Form.class );
if ( form != null ) {
form.newSingleLineResultsReporter( titlePane );
}
//Register delegating listener
delegatingApplicationListener = details.lookup( ApplicationListener.class );
//Insert the component
Component component;
DetailsComponentFactory componentFactory = details.lookup( DetailsComponentFactory.class );
if ( componentFactory != null ) {
component = componentFactory.createComponent();
} else {
component = details.lookup( Component.class );
if ( component == null ) {
throw new IllegalStateException( "No componentFactory and no component provided for " + selectedObject );
}
}
return component;
}
@Override
public void onApplicationEvent( ApplicationEvent event ) {
if ( delegatingApplicationListener != null ) {
delegatingApplicationListener.onApplicationEvent( event );
}
}
protected void updateContentPanelBorder() {
assert contentPanel.getComponentCount() == 1;
Component centerComponent = contentPanel.getComponent( 0 );
if ( centerComponent instanceof JScrollPane ) {
contentPanel.setBorder( BorderFactory.createEmptyBorder() );
} else {
contentPanel.setBorder( BorderFactory.createEmptyBorder( 5, 5, 5, 5 ) );
}
}
@NotNull
private final Renderer<Object, Object> fallbackRenderer = new TranslateClassNameRenderer();
@NotNull
protected Renderer<Object, Object> getRenderer( @NotNull HierarchyLayer<Object, ?> layer ) {
//noinspection unchecked
Renderer<Object, Object> renderer = layer.getLookup().lookup( Renderer.class );
if ( renderer == null ) {
return fallbackRenderer;
} else {
return renderer;
}
}
@NotNull
public JPanel getContentPanel() {
return contentPanel;
}
/**
* Returns the title pane control.
* The title pane control is shown at the top of the details panel
*
* @return the title pane control
*/
@NotNull
public JComponent getTopPanel() {
return topPanel;
}
@NotNull
public PageComponentContext getContext() {
if ( context == null ) {
throw new IllegalStateException( "Context not initialized" );
}
return context;
}
public void setContext( @NotNull PageComponentContext context ) {
if ( this.context != null ) {
throw new IllegalStateException( "context still initialized" );
}
this.context = context;
}
@Nullable
public Object getCurrentlySelectedObject() {
return currentlySelectedObject;
}
private PageComponentContext context;
//Spring support delegates
public String getMessage( @NotNull @NonNls String messageCode ) {
return SpringSupport.INSTANCE.getMessage( messageCode );
}
public ComponentFactory getComponentFactory() {
return SpringSupport.INSTANCE.getComponentFactory();
}
public <S> S getService( Class<S> serviceType ) {
return SpringSupport.INSTANCE.getService( serviceType );
}
public ApplicationServices getApplicationServices() {
return SpringSupport.INSTANCE.getApplicationServices();
}
public String getMessage( @NotNull @NonNls String messageCode, Object... objects ) {
return SpringSupport.INSTANCE.getMessage( messageCode, objects );
}
public Application getApplication() {
return SpringSupport.INSTANCE.getApplication();
}
public ApplicationContext getApplicationContext() {
return SpringSupport.INSTANCE.getApplicationContext();
}
public void publishEvent( @NotNull ApplicationEvent event ) {
SpringSupport.INSTANCE.publishEvent( event );
}
public void publishCreated( @NotNull Object object ) {
SpringSupport.INSTANCE.publishCreated( object );
}
public void publishDeleted( @NotNull Object object ) {
SpringSupport.INSTANCE.publishDeleted( object );
}
public void publishModified( @NotNull Object object ) {
SpringSupport.INSTANCE.publishModified( object );
}
}