package com.cedarsoft.spring.rcp.tbpanel;
import com.cedarsoft.spring.SpringSupport;
import com.cedarsoft.spring.rcp.ComponentProvider;
import com.cedarsoft.spring.rcp.PageComponentContextAware;
import com.cedarsoft.spring.rcp.table.ExtendedObjectTable;
import com.cedarsoft.spring.rcp.table.ObjectTable;
import com.cedarsoft.spring.rcp.tbpanel.aspects.ObjectTablePanelAspect;
import com.cedarsoft.spring.rcp.tbpanel.config.ObjectTableConfigurer;
import org.jdesktop.swingx.VerticalLayout;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.binding.value.ValueModel;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.richclient.application.PageComponentContext;
import org.springframework.richclient.application.ServiceNotFoundException;
import org.springframework.richclient.application.config.ApplicationObjectConfigurer;
import org.springframework.richclient.command.ActionCommand;
import org.springframework.richclient.command.CommandServices;
import org.springframework.richclient.command.config.CommandConfigurer;
import org.springframework.richclient.command.config.CommandFaceDescriptor;
import org.springframework.richclient.core.UIConstants;
import org.springframework.richclient.factory.ButtonFactory;
import org.springframework.richclient.factory.ComponentFactory;
import org.springframework.richclient.list.ListSelectionValueModelAdapter;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* A panel that contains a table and some buttons on the right side.
* It is necessary to call {@link #setObjectTable(ObjectTable)} to fill the panel with content.
*
* @param <T> the type of the entries that are shown within the table
*/
public class ObjectTablePanel<T> extends JPanel implements PageComponentContextAware, ApplicationListener {
@NotNull
@NonNls
private static final String COMMANDS_FACE_ID = "commandsBar";
/**
* Do not use directly. Use {@link #getObjectTable()} instead.
*/
private ObjectTable<T> objectTable;
@NotNull
@NonNls
protected final String objectId;
@NotNull
private final List<ObjectTableConfigurer<T>> configurers = new ArrayList<ObjectTableConfigurer<T>>();
@NotNull
private final List<SidePanelCommandsProvider> commandsProviders = new ArrayList<SidePanelCommandsProvider>();
/**
* Creates a new table based panel for the given object id
*
* @param objectId the object id
*/
public ObjectTablePanel( @NotNull @NonNls String objectId ) {
super( new BorderLayout( UIConstants.ONE_SPACE, UIConstants.ONE_SPACE ) );
this.objectId = objectId;
}
/**
* Applies the given aspect
*
* @param aspect the aspect that is applied
*/
public void applyAspect( @NotNull ObjectTablePanelAspect<T> aspect ) {
aspect.apply( this );
}
/**
* Returns the object table (if set)
*
* @return the object table
* @throws IllegalStateException
*/
@NotNull
public final ObjectTable<T> getObjectTable() throws IllegalStateException {
if ( objectTable == null ) {
throw new IllegalStateException( "Set the objectTable first" );
}
return objectTable;
}
/**
* Sets the object table
*
* @param objectTable the table
*/
public final void setObjectTable( @NotNull ObjectTable<T> objectTable ) {
//noinspection InstanceVariableUsedBeforeInitialized
if ( this.objectTable != null ) {
throw new IllegalStateException( "Table still has been set!" );
}
this.objectTable = objectTable;
configureTablePreShow();
addComponents();
configureTablePostShow();
}
/**
* This method is called from {@link #setObjectTable(ObjectTable)}.
* It creates and adds all components.
*/
private void addComponents() {
//add the header component
addHeadComponent();
//add the tail component
addTailComponent();
//And now add the objectTable --> this must be added last, since the head component could add some filters
addTableComponent();
//Now add the commands --> they depend on the final list of the object table
addCommands();
}
//Add the tail component
private void addTailComponent() {
if ( !tailComponentProviders.isEmpty() ) {
add( createProvidersPanel( tailComponentProviders ), BorderLayout.SOUTH );
}
}
/**
* Creates a vertical panel that contains all components created by the given providers
*
* @param providers the providers that are used to create the panel
* @return a panel containing all panels created by the providers
*/
@NotNull
private JPanel createProvidersPanel( @NotNull List<? extends ComponentProvider> providers ) {
if ( providers.isEmpty() ) {
throw new IllegalArgumentException( "Need at least one provider" );
}
JPanel panel = getComponentFactory().createPanel( new VerticalLayout( UIConstants.ONE_SPACE ) );
for ( ComponentProvider provider : providers ) {
panel.add( provider.createComponent() );
}
return panel;
}
/**
* Add the head component
*/
private void addHeadComponent() {
if ( !headComponentProviders.isEmpty() ) {
List<ComponentProvider> providers = new ArrayList<ComponentProvider>( headComponentProviders );
Collections.reverse( providers );
add( createProvidersPanel( providers ), BorderLayout.NORTH );
}
}
/**
* Adds the commands
*/
private void addCommands() {
List<Collection<? extends ActionCommand>> commands = getCommands( new ListSelectionValueModelAdapter( getObjectTable().getSelectionModel() ) );
if ( commands.isEmpty() ) {
//No commands --> exit fast
return;
}
//todo fix spring
CommandConfigurer commandConfigurer = getService( CommandConfigurer.class );
ApplicationObjectConfigurer configurer = getService( ApplicationObjectConfigurer.class );
ButtonFactory bf = getToolBarButtonFactory();
CommandsBarButtonConfigurer commandsBarButtonConfigurer = new CommandsBarButtonConfigurer();
//Now create the button panel
// JPanel buttonPanel = getComponentFactory().createPanel( new VerticalLayout( UIConstants.ONE_SPACE ) );
JPanel buttonPanel = getComponentFactory().createPanel( new VerticalLayout() );
for ( Iterator<Collection<? extends ActionCommand>> it = commands.iterator(); it.hasNext(); ) {
Collection<? extends ActionCommand> commandsCollection = it.next();
for ( ActionCommand command : commandsCollection ) {
commandConfigurer.configure( command );
CommandFaceDescriptor face = new CommandFaceDescriptor();
configurer.configure( face, command.getId() + '.' + COMMANDS_FACE_ID );
command.setFaceDescriptor( COMMANDS_FACE_ID, face );
buttonPanel.add( command.createButton( COMMANDS_FACE_ID, bf, commandsBarButtonConfigurer ) );
}
if ( it.hasNext() ) {
JPanel spacer = new JPanel();
spacer.setPreferredSize( new Dimension( 2, 2 ) );
buttonPanel.add( spacer );
}
}
add( buttonPanel, BorderLayout.EAST );
}
/**
* Returns the commands that are shown in the button bar on the right side of the table
*
* @param selectionHolder the selection holder
* @return the command shown on the right side of the table
*/
@NotNull
private List<Collection<? extends ActionCommand>> getCommands( @NotNull ListSelectionValueModelAdapter selectionHolder ) {
List<Collection<? extends ActionCommand>> commands = new ArrayList<Collection<? extends ActionCommand>>();
for ( SidePanelCommandsProvider commandsProvider : commandsProviders ) {
Collection<? extends ActionCommand> commandsForProvider = commandsProvider.getCommands( selectionHolder );
if ( commandsForProvider.isEmpty() ) {
continue;
}
commands.add( commandsForProvider );
}
return commands;
}
@NotNull
protected ButtonFactory getToolBarButtonFactory() {
CommandServices commandServices = getService( CommandServices.class );
return commandServices.getToolBarButtonFactory();
}
/**
* Adds the table compoennt
*/
private void addTableComponent() {
add( getComponentFactory().createScrollPane( getObjectTable().getControl() ), BorderLayout.CENTER );
}
/**
* Configures the table. This method is called from {@link #setObjectTable(ObjectTable)}.
*/
private void configureTablePreShow() {
for ( ObjectTableConfigurer<T> configurer : configurers ) {
configurer.configurePreShow( objectTable );
}
}
private void configureTablePostShow() {
for ( ObjectTableConfigurer<T> configurer : configurers ) {
configurer.configurePostShow( objectTable );
}
}
@Override
public void onApplicationEvent( ApplicationEvent event ) {
if ( objectTable != null ) {
objectTable.onApplicationEvent( event );
}
}
@Nullable
public List<? extends ObjectTableConfigurer<T>> getConfigurers() {
return Collections.unmodifiableList( configurers );
}
public final void addConfigurer( @NotNull ObjectTableConfigurer<T> configurer ) {
//noinspection InstanceVariableUsedBeforeInitialized
if ( this.objectTable != null ) {
throw new IllegalStateException( "Configurer should only be set if the object table is null" );
}
configurers.add( configurer );
}
@NotNull
public List<? extends SidePanelCommandsProvider> getCommandProviders() {
return Collections.unmodifiableList( commandsProviders );
}
/**
* Adds a command provider for the given panel
*
* @param commandsProvider the command provider that is added
*/
public final void addCommandProvider( @NotNull SidePanelCommandsProvider commandsProvider ) {
//noinspection InstanceVariableUsedBeforeInitialized
if ( this.objectTable != null ) {
throw new IllegalStateException( "SidePanelCommandsProvider should only be set if the object table is null" );
}
commandsProviders.add( commandsProvider );
}
/**
* Updates the page component context
*
* @param context the context
*/
@Override
public void updateContext( @NotNull PageComponentContext context ) {
if ( objectTable != null ) {
getObjectTable().setStatusBar( context.getWindow().getStatusBar() );
}
for ( SidePanelCommandsProvider commandsProvider : commandsProviders ) {
commandsProvider.registerLocalCommandExecutors( context );
}
registerLocalCommandExecutors( context );
contextUpdated( context );
}
/**
* This method is called when the context has been updated
*
* @param context the new context
*/
@Deprecated
protected void contextUpdated( @NotNull PageComponentContext context ) {
}
/**
* @param context
* @deprecated the commands providers are automatically called
*/
@Deprecated
protected void registerLocalCommandExecutors( @NotNull PageComponentContext context ) {
}
@NotNull
private List<ComponentProvider> headComponentProviders = new ArrayList<ComponentProvider>();
@NotNull
public List<? extends ComponentProvider> getHeadComponentProviders() {
return Collections.unmodifiableList( headComponentProviders );
}
@Deprecated
@Nullable
public ComponentProvider getHeadComponentProvider() {
if ( headComponentProviders.isEmpty() ) {
return null;
} else {
return headComponentProviders.get( 0 );
}
}
/**
* Adds a head component provider.
* Those providers are added in reversed order!
* This means the component created by the first added provider is added at the *bottom* of the
* head component.
*
* @param headComponentProvider the provider
*/
public void addHeadComponentProvider( @Nullable ComponentProvider headComponentProvider ) {
this.headComponentProviders.add( headComponentProvider );
}
@Deprecated
public void setHeadComponentProvider( @Nullable ComponentProvider headComponentProvider ) {
addHeadComponentProvider( headComponentProvider );
}
@NotNull
private List<ComponentProvider> tailComponentProviders = new ArrayList<ComponentProvider>();
@NotNull
public List<? extends ComponentProvider> getTailComponentProviders() {
return Collections.unmodifiableList( tailComponentProviders );
}
@Deprecated
@Nullable
public ComponentProvider getTailComponentProvider() {
if ( tailComponentProviders.isEmpty() ) {
return null;
} else {
return tailComponentProviders.get( 0 );
}
}
@Deprecated
public void setTailComponentProvider( @Nullable ComponentProvider tailComponentProvider ) {
addTailComponentProvider( tailComponentProvider );
}
public void addTailComponentProvider( @Nullable ComponentProvider tailComponentProvider ) {
this.tailComponentProviders.add( tailComponentProvider );
}
//// SPRING DELEGATES ////
@NotNull
protected String getMessage( @NotNull @NonNls String messageCode ) {
return SpringSupport.INSTANCE.getMessage( messageCode );
}
@NotNull
protected String getMessage( @NotNull @NonNls String messageCode, @NonNls Object... objects ) {
return SpringSupport.INSTANCE.getMessage( messageCode, objects );
}
@NotNull
protected 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 );
}
@NotNull
protected ComponentFactory getComponentFactory() throws ServiceNotFoundException {
return getService( ComponentFactory.class );
}
@NotNull
protected <T> T getService( @NotNull Class<T> serviceType ) throws ServiceNotFoundException {
return SpringSupport.INSTANCE.getService( serviceType );
}
}