Revision: 1264
Author: mathiasbr
Date: 2006-08-04 07:14:49 -0700 (Fri, 04 Aug 2006)
ViewCVS: http://svn.sourceforge.net/spring-rich-c/?rev=1264&view=rev
Log Message:
-----------
AbstractObjectTable moved from simple sample to sandbox
Modified Paths:
--------------
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/ContactTable.java
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/ContactView.java
Added Paths:
-----------
trunk/spring-richclient/sandbox/src/main/java/org/springframework/richclient/application/support/
trunk/spring-richclient/sandbox/src/main/java/org/springframework/richclient/application/support/ApplicationEventRedispatcher.java
trunk/spring-richclient/sandbox/src/main/java/org/springframework/richclient/table/support/AbstractObjectTable.java
Removed Paths:
-------------
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/AbstractObjectTable.java
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/ApplicationEventRedispatcher.java
Deleted:
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/AbstractObjectTable.java
===================================================================
---
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/AbstractObjectTable.java
2006-08-03 13:00:28 UTC (rev 1263)
+++
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/AbstractObjectTable.java
2006-08-04 14:14:49 UTC (rev 1264)
@@ -1,608 +0,0 @@
-/*
- * Copyright 2002-2006 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
under
- * the License.
- */
-package org.springframework.richclient.samples.simple.ui;
-
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.util.Arrays;
-
-import javax.swing.JPopupMenu;
-import javax.swing.JTable;
-import javax.swing.ListSelectionModel;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.support.PropertyComparator;
-import org.springframework.binding.value.ValueModel;
-import org.springframework.context.ApplicationEvent;
-import org.springframework.context.ApplicationListener;
-import
org.springframework.richclient.application.event.LifecycleApplicationEvent;
-import
org.springframework.richclient.application.support.ApplicationServicesAccessor;
-import org.springframework.richclient.command.ActionCommandExecutor;
-import org.springframework.richclient.command.CommandGroup;
-import org.springframework.richclient.command.GuardedActionCommandExecutor;
-import org.springframework.richclient.list.ListSelectionValueModelAdapter;
-import org.springframework.richclient.progress.StatusBarCommandGroup;
-import org.springframework.richclient.table.support.GlazedTableModel;
-import org.springframework.richclient.util.PopupMenuMouseListener;
-
-import ca.odell.glazedlists.BasicEventList;
-import ca.odell.glazedlists.EventList;
-import ca.odell.glazedlists.SortedList;
-import ca.odell.glazedlists.event.ListEvent;
-import ca.odell.glazedlists.event.ListEventListener;
-import ca.odell.glazedlists.gui.TableFormat;
-import ca.odell.glazedlists.swing.EventSelectionModel;
-import ca.odell.glazedlists.swing.TableComparatorChooser;
-
-/**
- * This class provides a standard table representation for a set of objects
with
- * properties of the objects presented in the columns of the table. The table
created
- * offers the following features:
- * <ol>
- * <li>It uses Glazed Lists as the underlying data model and this provides for
- * multi-column sorting and text filtering.</li>
- * <li>It handles row selection.</>
- * <li>It offers simple, delegated handling of how to handle a double-click on
a row, by
- * setting a command executor. See [EMAIL PROTECTED]
#setDoubleClickHandler(ActionCommandExecutor)}.</li>
- * <li>It supports display of a configured pop-up context menu.</li>
- * <li>Offers the selection model up as a ValueModel, using
- * [EMAIL PROTECTED] #getTableSelectionHolder()} so that Guards can be
constructed based on the
- * selection contents.</li>
- * <li>It can report on row counts (after filtering) and selection counts to a
status bar</li>
- * </ol>
- * <p>
- * Several I18N messages are needed for proper reporting to a configured
status bar. The
- * message keys used are:
- * <p>
- * <table border="1">
- * <tr>
- * <td><b>Message key </b></td>
- * <td><b>Usage </b></td>
- * </tr>
- * <tr>
- * <td><i>modelId</i>.objectName.singular</td>
- * <td>The singular name of the objects in the table</td>
- * </tr>
- * <tr>
- * <td><i>modelId</i>.objectName.plural</td>
- * <td>The plural name of the objects in the table</td>
- * </tr>
- * <tr>
- * <td><i>[modelId]</i>.objectTable.showingAll.message</td>
- * <td>The message to show when all objects are being shown, that is no
objects have been
- * filtered. This is typically something like "Showing all nn contacts". The
message takes
- * the number of objects nd the object name (singular or plural) as
parameters.</td>
- * </tr>
- * <tr>
- * <td><i>[modelId]</i>.objectTable.showingN.message</td>
- * <td>The message to show when some of the objects have been filtered from
the display.
- * This is typically something like "Showing nn contacts of nn". The message
takes the
- * shown count, the total count, and the object name (singular or plural) as
parameters.</td>
- * </tr>
- * <tr>
- * <td><i>[modelId]</i>.objectTable.selectedN.message</td>
- * <td>The message to append to the filter message when the selection is not
empty.
- * Typically something like ", nn selected". The message takes the number of
selected
- * entries as a parameter.</td>
- * </tr>
- * </table>
- * <p>
- * Note that the message keys that show the model id in brackets, like this
<i>[modelId]</i>,
- * indicate that the model id is optional. If no message is found using the
model id, then
- * the key will be tried without the model id and the resulting string will be
used. This
- * makes it easy to construct one single message property that can be used on
numerous
- * tables.
- * <p>
- * <em>Note:</em> If you are using application events to inform UI components
of changes
- * to domain objects, then instances of this class have to be wired into the
event
- * distribution. To do this, you should construct instances (of concrete
subclasses) in
- * the application context. They will automatically be wired into the
epplication event
- * mechanism because this class implements [EMAIL PROTECTED]
ApplicationListener}.
- *
- * @author Larry Streepy
- */
-public abstract class AbstractObjectTable extends ApplicationServicesAccessor
implements ListEventListener {
-
- private final Log _logger = LogFactory.getLog(getClass());
-
- private String modelId;
- private String objectSingularName;
- private String objectPluralName;
- private Object[] initialData = null;
- private String[] columnPropertyNames;
- private GlazedTableModel model;
- private JTable table;
- private SortedList baseList;
- private EventList finalEventList;
- private ActionCommandExecutor doubleClickHandler;
- private CommandGroup popupCommandGroup;
- private StatusBarCommandGroup statusBar;
-
- public static final String SHOWINGALL_MSG_KEY =
"objectTable.showingAll.message";
- public static final String SHOWINGN_MSG_KEY =
"objectTable.showingN.message";
- public static final String SELECTEDN_MSG_KEY =
"objectTable.selectedN.message";
-
- /**
- * Constructor.
- *
- * @param modelId used for generating message keys
- * @param objectType The type of object held in the table
- */
- public AbstractObjectTable( String modelId, String[] columnPropertyNames )
{
- this.modelId = modelId;
- setColumnPropertyNames(columnPropertyNames);
- init();
- }
-
- /**
- * Initialize our internal values.
- */
- protected void init() {
- // Get all our messages
-
- objectSingularName = getMessage(modelId + ".objectName.singular");
- objectPluralName = getMessage(modelId + ".objectName.plural");
- }
-
- /**
- * Set the initial data to display.
- *
- * @param initialData Array of objects to display
- */
- public void setInitialData( Object[] initialData ) {
- this.initialData = initialData;
- }
-
- /**
- * Get the initial data to display. If none has been set, then return the
default
- * initial data.
- *
- * @return initial data to display
- * @see #getDefaultInitialData()
- */
- public Object[] getInitialData() {
- if( initialData == null ) {
- initialData = getDefaultInitialData();
- }
- return initialData;
- }
-
- /**
- * Get the base event list for the table model. This can be used to build
layered
- * event models for filtering.
- *
- * @return base event list
- */
- public EventList getBaseEventList() {
- if( baseList == null ) {
- // Construct on demand
- Object[] data = getInitialData();
-
- if( _logger.isInfoEnabled() ) {
- _logger.info("Table data: got " + data.length + " entries");
- }
-
- // Construct the event list of all our data and layer on the
sorting
- EventList rawList = new BasicEventList();
- rawList.addAll(Arrays.asList(data));
- String sortProperty =
getColumnPropertyNames()[getInitialSortColumn()];
- baseList = new SortedList(rawList, new
PropertyComparator(sortProperty, false, true));
- }
- return baseList;
- }
-
- /**
- * Set the event list to be used for constructing the table model. The
event list
- * provided MUST have been constructed from the list returned by
- * [EMAIL PROTECTED] #getBaseEventList()} or this table will not work
properly.
- *
- * @param event list to use
- */
- public void setFinalEventList( EventList finalEventList ) {
- this.finalEventList = finalEventList;
- }
-
- /**
- * Get the event list to be use for constructing the table model.
- *
- * @return final event list
- */
- public EventList getFinalEventList() {
- if( finalEventList == null ) {
- finalEventList = getBaseEventList();
- }
- return finalEventList;
- }
-
- /**
- * Get the table control.
- *
- * @return JTable instance all configured and ready to go
- */
- public JTable getTable() {
- if( table == null ) {
- createTable();
- }
- return table;
- }
-
- /**
- * Create our control.
- */
- public void createTable() {
-
- // Contstruct the table model and table to display the data
- EventList finalEventList = getFinalEventList();
- model = createTableModel(finalEventList);
-
- table = new JTable(model);
- table.setSelectionModel(new EventSelectionModel(finalEventList));
- table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
-
- // Install the sorter
- TableComparatorChooser tableSorter = new TableComparatorChooser(table,
baseList, true);
-
- // Allow the derived type to configure the table
- configureTable(table);
-
- // Sort on the last name by default
- tableSorter.clearComparator();
- tableSorter.appendComparator(getInitialSortColumn(), 0, false);
-
- // Add the context menu listener
- table.addMouseListener(new PopupMenuMouseListener() {
- protected JPopupMenu getPopupMenu() {
- return createPopupContextMenu();
- }
- });
-
- // Add our mouse handlers to setup our desired selection mechanics
- table.addMouseListener(new MouseAdapter() {
- public void mousePressed( MouseEvent e ) {
- // If the user right clicks on a row other than the selection,
- // then move
- // the selection to the current row
- int rowUnderMouse = table.rowAtPoint(e.getPoint());
- if( e.getButton() == MouseEvent.BUTTON3 &&
!table.isRowSelected(rowUnderMouse) ) {
- // Select the row under the mouse
- if( rowUnderMouse != -1 ) {
-
table.getSelectionModel().setSelectionInterval(rowUnderMouse, rowUnderMouse);
- }
- }
- }
-
- /**
- * Handle double click.
- */
- public void mouseClicked( MouseEvent e ) {
- // If the user double clicked on a row, then open the
properties
- if( e.getClickCount() == 2 ) {
- onDoubleClick();
- }
- }
- });
-
- // Keep our status line up to date with the selections and filtering
- getSelectionModel().addListSelectionListener(new
ListSelectionListener() {
- public void valueChanged( ListSelectionEvent e ) {
- updateStatusBar();
- }
- });
-
- getFinalEventList().addListEventListener(this);
- }
-
- /**
- * Get the value model holding the table selection.
- */
- protected ValueModel getTableSelectionHolder() {
- return new
ListSelectionValueModelAdapter(getTable().getSelectionModel());
- }
-
- /**
- * Handle a double click on a row of the table. The row will already be
selected.
- */
- protected void onDoubleClick() {
- // Dispatch this to the doubleClickHandler, if any
- if( doubleClickHandler != null ) {
- boolean okToExecute = true;
- if( doubleClickHandler instanceof GuardedActionCommandExecutor ) {
- okToExecute = ((GuardedActionCommandExecutor)
doubleClickHandler).isEnabled();
- }
-
- if( okToExecute ) {
- doubleClickHandler.execute();
- }
- }
- }
-
- /**
- * Construct the table model for this table. The default implementation of
this
- * creates a GlazedTableModel using an Advanced format.
- *
- * @param eventList on which to build the model
- * @return table model
- */
- protected GlazedTableModel createTableModel( EventList eventList ) {
- return new GlazedTableModel(eventList, getMessageSource(),
getColumnPropertyNames()) {
- protected TableFormat createTableFormat() {
- return new DefaultAdvancedTableFormat();
- }
- };
- }
-
- /**
- * Get the data model for the table.
- *
- * @return model
- */
- public GlazedTableModel getTableModel() {
- return model;
- }
-
- /**
- * Get the names of the properties to display in the table columns.
- *
- * @return array of columnproperty names
- */
- public String[] getColumnPropertyNames() {
- return columnPropertyNames;
- }
-
- /**
- * Set the names of the properties to display in the table columns.
- *
- * @param columnPropertyNames
- */
- public void setColumnPropertyNames( String[] columnPropertyNames ) {
- this.columnPropertyNames = columnPropertyNames;
- }
-
- /**
- * @return the doubleClickHandler
- */
- public ActionCommandExecutor getDoubleClickHandler() {
- return doubleClickHandler;
- }
-
- /**
- * Set the handler (action executor) that should be invoked when a row in
the table is
- * double-clicked.
- *
- * @param doubleClickHandler the doubleClickHandler to set
- */
- public void setDoubleClickHandler( ActionCommandExecutor
doubleClickHandler ) {
- this.doubleClickHandler = doubleClickHandler;
- }
-
- /**
- * @return the popupCommandGroup
- */
- public CommandGroup getPopupCommandGroup() {
- return popupCommandGroup;
- }
-
- /**
- * Set the command group that should be used to construct the popup menu
when a user
- * initiates the UI gesture to show the context menu. If this is null,
then no popup
- * menu will be shown.
- *
- * @param popupCommandGroup the popupCommandGroup to set
- */
- public void setPopupCommandGroup( CommandGroup popupCommandGroup ) {
- this.popupCommandGroup = popupCommandGroup;
- }
-
- /**
- * Configure the newly created table as needed. Install any needed column
sizes,
- * renderers, and comparators. The default implementation does nothing.
- *
- * @param table The table to configure
- */
- protected void configureTable( JTable table ) {
- }
-
- /**
- * Get the default set of objects for this table.
- *
- * @return Array of data for the table
- */
- protected abstract Object[] getDefaultInitialData();
-
- /**
- * Determine if the event should be handled on this table. If
<code>true</code> is
- * returned (the default), then the list holding the table data will be
scanned for
- * the object and updated appropriately depending on then event type.
- *
- * @param event to inspect
- * @return boolean true if the object should be handled, false otherwise
- * @see #handleDeletedObject(Object)
- * @see #handleNewObject(Object)
- * @see #handleUpdatedObject(Object)
- *
- */
- protected boolean shouldHandleEvent( ApplicationEvent event ) {
- return true;
- }
-
- /**
- * Create the context popup menu, if any, for this table. The default
operation is to
- * create the popup from the command group if one has been specified. If
not, then
- * null is returned.
- *
- * @return popup menu to show, or null if none
- */
- protected JPopupMenu createPopupContextMenu() {
- return (getPopupCommandGroup() != null) ?
getPopupCommandGroup().createPopupMenu() : null;
- }
-
- /**
- * Get the default sort column. Defaults to 0.
- *
- * @return column to sort on
- */
- protected int getInitialSortColumn() {
- return 0;
- }
-
- /**
- * Get the selection model.
- *
- * @return selection model
- */
- public ListSelectionModel getSelectionModel() {
- return table.getSelectionModel();
- }
-
- /**
- * Handle the creation of a new object.
- *
- * @param object New object to handle
- */
- protected void handleNewObject( Object object ) {
- getFinalEventList().add(object);
- }
-
- /**
- * Handle an updated object in this table. Locate the existing entry (by
equals) and
- * replace it in the underlying list.
- *
- * @param object Updated object to handle
- */
- protected void handleUpdatedObject( Object object ) {
- int index = baseList.indexOf(object);
- if( index >= 0 ) {
- baseList.set(index, object);
- }
- }
-
- /**
- * Handle the deletion of an object in this table. Locate this entry (by
equals) and
- * delete it.
- *
- * @param object Updated object being deleted
- */
- protected void handleDeletedObject( Object object ) {
- int index = baseList.indexOf(object);
- if( index >= 0 ) {
- baseList.remove(index);
- }
- }
-
- /**
- * Set the status bar associated with this table. If non-null, then any
time the final
- * event list on this table changes, then the status bar will be updated
with the
- * current object counts.
- *
- * @param statusBar to update
- */
- protected void reportToStatusBar( StatusBarCommandGroup statusBar ) {
- this.statusBar = statusBar;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
ca.odell.glazedlists.event.ListEventListener#listChanged(ca.odell.glazedlists.event.ListEvent)
- */
- public void listChanged( ListEvent listChanges ) {
- // Our object list has changed, so update the status bar
- updateStatusBar();
- }
-
- /**
- * Update the status bar with the current display counts.
- */
- protected void updateStatusBar() {
- if( statusBar != null ) {
- int all = getBaseEventList().size();
- int showing = getFinalEventList().size();
- String msg;
- if( all == showing ) {
- String[] keys = new String[] { modelId + "." +
SHOWINGALL_MSG_KEY, SHOWINGALL_MSG_KEY };
- msg = getMessage(keys, new Object[] { ""+all, (all != 1) ?
objectPluralName : objectSingularName } );
- } else {
- String[] keys = new String[] { modelId + "." +
SHOWINGN_MSG_KEY, SHOWINGN_MSG_KEY };
-
- msg = getMessage(keys, new Object[] { ""+showing, (showing !=
1) ? objectPluralName : objectSingularName, ""+all } );
- }
-
- // Now add the selection info
- int nselected = table.getSelectedRowCount();
- if( nselected > 0 ) {
- String[] keys = new String[] { modelId + "." +
SELECTEDN_MSG_KEY, SELECTEDN_MSG_KEY };
-
- msg += getMessage(keys, new Object[] { ""+nselected } );
- }
-
- statusBar.setMessage(msg.toString());
- }
- }
-
- /**
- * Set the event redispatcher. This will be configured via the application
context.
- * When this is set, we add our handler so that we will receive proper
application
- * event notification.
- *
- * @param dispatcher
- */
- public void setApplicationEventRedispatcher( ApplicationEventRedispatcher
dispatcher ) {
- dispatcher.addListener(new ApplicationEventHandler());
- }
-
- /**
- * This class will handle the application event dispatching. It might seem
correct to
- * just have the main class implement ApplicationListener, but that will
lead to
- * problems since instances of this class are commonly created in the
application
- * context as prototypes (non-singletons) which leads to an extra instance
being
- * created that is not fully initialized.
- * <p>
- * This stems from the way that the ApplicationListener tag interface is
handled
- * within the application context. After all the bean definitions have been
- * internalized, the context looks for any beans that implement the
- * ApplicationListener interface. It then obtains the bean and registers
it as a
- * listener. Since object tables are typically prototypes, this creates an
instance of
- * the table that is not tied into any other object and has not been
completely
- * initialized.
- * <p>
- * So, to avoid all that, we simply use an internal class to handle the
events.
- */
- private class ApplicationEventHandler implements ApplicationListener {
- /**
- * Handle an application event. This will notify us of object adds,
deletes, and
- * modifications. Update our table model accordingly.
- *
- * @param e event to process
- */
- public void onApplicationEvent( ApplicationEvent e ) {
- if( e instanceof LifecycleApplicationEvent ) {
- LifecycleApplicationEvent le = (LifecycleApplicationEvent) e;
- if( shouldHandleEvent(e) ) {
- if( le.getEventType() == LifecycleApplicationEvent.CREATED
) {
- handleNewObject(le.getObject());
- } else if( le.getEventType() ==
LifecycleApplicationEvent.MODIFIED ) {
- handleUpdatedObject(le.getObject());
- } else if( le.getEventType() ==
LifecycleApplicationEvent.DELETED ) {
- handleDeletedObject(le.getObject());
- }
- }
- }
- }
- }
-}
Deleted:
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/ApplicationEventRedispatcher.java
===================================================================
---
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/ApplicationEventRedispatcher.java
2006-08-03 13:00:28 UTC (rev 1263)
+++
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/ApplicationEventRedispatcher.java
2006-08-04 14:14:49 UTC (rev 1264)
@@ -1,83 +0,0 @@
-/*
- * Copyright 2002-2006 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
under
- * the License.
- */
-package org.springframework.richclient.samples.simple.ui;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-
-import org.springframework.context.ApplicationEvent;
-import org.springframework.context.ApplicationListener;
-
-/**
- * This class is responsible for re-dispatching application events to a
collection of
- * additional registered listeners. This is needed due to the way that the
application
- * context handles the ApplicationListener tag interface. For any beans that
are
- * prototypes, one instance will be registered as an ApplicationListener and
all the
- * others will not. Further, there is no mechanism to get a reference to the
context's
- * event dispatcher to add listeners at runtime.
- * <p>
- * By defining a bean with this type in the context, it will be added as a
typical event
- * listener. Then, this dispatcher can be set on any other bean (including
prototypes)
- * that need to be added to the event mechanism. In order to wire it all
together, any
- * object that wants to register for events simply defines a property of type
- * ApplicationEventDispatcher and in the property setter method, call the
- * [EMAIL PROTECTED] #addListener(ApplicationListener)} method on itself (or a
delegate handler).
- * Like this:
- *
- * <pre>
- * public void setApplicationEventRedispatcher( ApplicationEventRedispatcher
dispatcher ) {
- * dispatcher.addListener(this);
- * }
- * </pre>
- *
- * @author Larry Streepy
- *
- */
-public class ApplicationEventRedispatcher implements ApplicationListener {
-
- /** Our list of delegates to dispatch events to. */
- private ArrayList delegates = new ArrayList();
-
- /**
- * Handle an application event, dispatch it to all our delegates.
- *
- * @param event to dispatch
- */
- public void onApplicationEvent( ApplicationEvent event ) {
- Iterator iter = delegates.iterator();
- while( iter.hasNext() ) {
- ((ApplicationListener) iter.next()).onApplicationEvent(event);
- }
- }
-
- /**
- * Add a listener to our set.
- *
- * @param l Listener to add
- */
- public void addListener( ApplicationListener l ) {
- delegates.add(l);
- }
-
- /**
- * Remove a listener from our set.
- *
- * @param l Listener to remove
- */
- public void removeListener( ApplicationListener l ) {
- delegates.remove(l);
- }
-}
Modified:
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/ContactTable.java
===================================================================
---
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/ContactTable.java
2006-08-03 13:00:28 UTC (rev 1263)
+++
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/ContactTable.java
2006-08-04 14:14:49 UTC (rev 1264)
@@ -20,6 +20,7 @@
import org.springframework.richclient.samples.simple.domain.Contact;
import org.springframework.richclient.samples.simple.domain.ContactDataStore;
+import org.springframework.richclient.table.support.AbstractObjectTable;
/**
* This class provides a concrete implementation of a table showing [EMAIL
PROTECTED] Contact}
Modified:
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/ContactView.java
===================================================================
---
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/ContactView.java
2006-08-03 13:00:28 UTC (rev 1263)
+++
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/ContactView.java
2006-08-04 14:14:49 UTC (rev 1264)
@@ -38,6 +38,7 @@
import
org.springframework.richclient.command.support.AbstractActionCommandExecutor;
import org.springframework.richclient.command.support.GlobalCommandIds;
import org.springframework.richclient.dialog.ConfirmationDialog;
+import org.springframework.richclient.list.ListSelectionValueModelAdapter;
import org.springframework.richclient.list.ListSingleSelectionGuard;
import org.springframework.richclient.samples.simple.domain.Contact;
import org.springframework.richclient.samples.simple.domain.ContactDataStore;
@@ -229,7 +230,7 @@
contactTable.setFinalEventList(filterList);
// Register to get notified when the filtered list changes
- contactTable.reportToStatusBar(getStatusBar());
+ contactTable.setStatusBar(getStatusBar());
// Ensure our commands are only active when something is selected.
// These guard objects operate by inspecting a list selection model
@@ -238,7 +239,7 @@
// This configuration greatly simplifies the interaction between
commands
// that require a selection on which to operate.
- ValueModel selectionHolder = contactTable.getTableSelectionHolder();
+ ValueModel selectionHolder = new
ListSelectionValueModelAdapter(contactTable.getSelectionModel());
new ListSingleSelectionGuard(selectionHolder, deleteExecutor);
new ListSingleSelectionGuard(selectionHolder, propertiesExecutor);
}
Copied:
trunk/spring-richclient/sandbox/src/main/java/org/springframework/richclient/application/support/ApplicationEventRedispatcher.java
(from rev 1259,
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/ApplicationEventRedispatcher.java)
===================================================================
---
trunk/spring-richclient/sandbox/src/main/java/org/springframework/richclient/application/support/ApplicationEventRedispatcher.java
(rev 0)
+++
trunk/spring-richclient/sandbox/src/main/java/org/springframework/richclient/application/support/ApplicationEventRedispatcher.java
2006-08-04 14:14:49 UTC (rev 1264)
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2002-2006 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package org.springframework.richclient.application.support;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+
+/**
+ * This class is responsible for re-dispatching application events to a
collection of
+ * additional registered listeners. This is needed due to the way that the
application
+ * context handles the ApplicationListener tag interface. For any beans that
are
+ * prototypes, one instance will be registered as an ApplicationListener and
all the
+ * others will not. Further, there is no mechanism to get a reference to the
context's
+ * event dispatcher to add listeners at runtime.
+ * <p>
+ * By defining a bean with this type in the context, it will be added as a
typical event
+ * listener. Then, this dispatcher can be set on any other bean (including
prototypes)
+ * that need to be added to the event mechanism. In order to wire it all
together, any
+ * object that wants to register for events simply defines a property of type
+ * ApplicationEventDispatcher and in the property setter method, call the
+ * [EMAIL PROTECTED] #addListener(ApplicationListener)} method on itself (or a
delegate handler).
+ * Like this:
+ *
+ * <pre>
+ * public void setApplicationEventRedispatcher( ApplicationEventRedispatcher
dispatcher ) {
+ * dispatcher.addListener(this);
+ * }
+ * </pre>
+ *
+ * @author Larry Streepy
+ *
+ */
+public class ApplicationEventRedispatcher implements ApplicationListener {
+
+ /** Our list of delegates to dispatch events to. */
+ private ArrayList delegates = new ArrayList();
+
+ /**
+ * Handle an application event, dispatch it to all our delegates.
+ *
+ * @param event to dispatch
+ */
+ public void onApplicationEvent( ApplicationEvent event ) {
+ Iterator iter = delegates.iterator();
+ while( iter.hasNext() ) {
+ ((ApplicationListener) iter.next()).onApplicationEvent(event);
+ }
+ }
+
+ /**
+ * Add a listener to our set.
+ *
+ * @param l Listener to add
+ */
+ public void addListener( ApplicationListener l ) {
+ delegates.add(l);
+ }
+
+ /**
+ * Remove a listener from our set.
+ *
+ * @param l Listener to remove
+ */
+ public void removeListener( ApplicationListener l ) {
+ delegates.remove(l);
+ }
+}
Copied:
trunk/spring-richclient/sandbox/src/main/java/org/springframework/richclient/table/support/AbstractObjectTable.java
(from rev 1259,
trunk/spring-richclient/samples/simple/src/main/java/org/springframework/richclient/samples/simple/ui/AbstractObjectTable.java)
===================================================================
---
trunk/spring-richclient/sandbox/src/main/java/org/springframework/richclient/table/support/AbstractObjectTable.java
(rev 0)
+++
trunk/spring-richclient/sandbox/src/main/java/org/springframework/richclient/table/support/AbstractObjectTable.java
2006-08-04 14:14:49 UTC (rev 1264)
@@ -0,0 +1,598 @@
+/*
+ * Copyright 2002-2006 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package org.springframework.richclient.table.support;
+
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.Arrays;
+
+import javax.swing.JPopupMenu;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.support.PropertyComparator;
+import org.springframework.binding.value.ValueModel;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import
org.springframework.richclient.application.event.LifecycleApplicationEvent;
+import
org.springframework.richclient.application.support.ApplicationEventRedispatcher;
+import
org.springframework.richclient.application.support.ApplicationServicesAccessor;
+import org.springframework.richclient.command.ActionCommandExecutor;
+import org.springframework.richclient.command.CommandGroup;
+import org.springframework.richclient.command.GuardedActionCommandExecutor;
+import org.springframework.richclient.list.ListSelectionValueModelAdapter;
+import org.springframework.richclient.progress.StatusBarCommandGroup;
+import org.springframework.richclient.util.PopupMenuMouseListener;
+
+import ca.odell.glazedlists.BasicEventList;
+import ca.odell.glazedlists.EventList;
+import ca.odell.glazedlists.SortedList;
+import ca.odell.glazedlists.event.ListEvent;
+import ca.odell.glazedlists.event.ListEventListener;
+import ca.odell.glazedlists.gui.TableFormat;
+import ca.odell.glazedlists.swing.EventSelectionModel;
+import ca.odell.glazedlists.swing.TableComparatorChooser;
+
+/**
+ * This class provides a standard table representation for a set of objects
with
+ * properties of the objects presented in the columns of the table. The table
created
+ * offers the following features:
+ * <ol>
+ * <li>It uses Glazed Lists as the underlying data model and this provides for
+ * multi-column sorting and text filtering.</li>
+ * <li>It handles row selection.</>
+ * <li>It offers simple, delegated handling of how to handle a double-click on
a row, by
+ * setting a command executor. See [EMAIL PROTECTED]
#setDoubleClickHandler(ActionCommandExecutor)}.</li>
+ * <li>It supports display of a configured pop-up context menu.</li>
+ * <li>It can report on row counts (after filtering) and selection counts to a
status bar</li>
+ * </ol>
+ * <p>
+ * Several I18N messages are needed for proper reporting to a configured
status bar. The
+ * message keys used are:
+ * <p>
+ * <table border="1">
+ * <tr>
+ * <td><b>Message key </b></td>
+ * <td><b>Usage </b></td>
+ * </tr>
+ * <tr>
+ * <td><i>modelId</i>.objectName.singular</td>
+ * <td>The singular name of the objects in the table</td>
+ * </tr>
+ * <tr>
+ * <td><i>modelId</i>.objectName.plural</td>
+ * <td>The plural name of the objects in the table</td>
+ * </tr>
+ * <tr>
+ * <td><i>[modelId]</i>.objectTable.showingAll.message</td>
+ * <td>The message to show when all objects are being shown, that is no
objects have been
+ * filtered. This is typically something like "Showing all nn contacts". The
message takes
+ * the number of objects nd the object name (singular or plural) as
parameters.</td>
+ * </tr>
+ * <tr>
+ * <td><i>[modelId]</i>.objectTable.showingN.message</td>
+ * <td>The message to show when some of the objects have been filtered from
the display.
+ * This is typically something like "Showing nn contacts of nn". The message
takes the
+ * shown count, the total count, and the object name (singular or plural) as
parameters.</td>
+ * </tr>
+ * <tr>
+ * <td><i>[modelId]</i>.objectTable.selectedN.message</td>
+ * <td>The message to append to the filter message when the selection is not
empty.
+ * Typically something like ", nn selected". The message takes the number of
selected
+ * entries as a parameter.</td>
+ * </tr>
+ * </table>
+ * <p>
+ * Note that the message keys that show the model id in brackets, like this
<i>[modelId]</i>,
+ * indicate that the model id is optional. If no message is found using the
model id, then
+ * the key will be tried without the model id and the resulting string will be
used. This
+ * makes it easy to construct one single message property that can be used on
numerous
+ * tables.
+ * <p>
+ * <em>Note:</em> If you are using application events to inform UI components
of changes
+ * to domain objects, then instances of this class have to be wired into the
event
+ * distribution. To do this, you should construct instances (of concrete
subclasses) in
+ * the application context. They will automatically be wired into the
epplication event
+ * mechanism because this class implements [EMAIL PROTECTED]
ApplicationListener}.
+ *
+ * @author Larry Streepy
+ */
+public abstract class AbstractObjectTable extends ApplicationServicesAccessor
implements ListEventListener {
+
+ private final Log _logger = LogFactory.getLog(getClass());
+
+ private String modelId;
+ private String objectSingularName;
+ private String objectPluralName;
+ private Object[] initialData = null;
+ private String[] columnPropertyNames;
+ private GlazedTableModel model;
+ private JTable table;
+ private SortedList baseList;
+ private EventList finalEventList;
+ private ActionCommandExecutor doubleClickHandler;
+ private CommandGroup popupCommandGroup;
+ private StatusBarCommandGroup statusBar;
+
+ public static final String SHOWINGALL_MSG_KEY =
"objectTable.showingAll.message";
+ public static final String SHOWINGN_MSG_KEY =
"objectTable.showingN.message";
+ public static final String SELECTEDN_MSG_KEY =
"objectTable.selectedN.message";
+
+ /**
+ * Constructor.
+ *
+ * @param modelId used for generating message keys
+ * @param objectType The type of object held in the table
+ */
+ public AbstractObjectTable( String modelId, String[] columnPropertyNames )
{
+ this.modelId = modelId;
+ setColumnPropertyNames(columnPropertyNames);
+ init();
+ }
+
+ /**
+ * Initialize our internal values.
+ */
+ protected void init() {
+ // Get all our messages
+
+ objectSingularName = getMessage(modelId + ".objectName.singular");
+ objectPluralName = getMessage(modelId + ".objectName.plural");
+ }
+
+ /**
+ * Set the initial data to display.
+ *
+ * @param initialData Array of objects to display
+ */
+ public void setInitialData( Object[] initialData ) {
+ this.initialData = initialData;
+ }
+
+ /**
+ * Get the initial data to display. If none has been set, then return the
default
+ * initial data.
+ *
+ * @return initial data to display
+ * @see #getDefaultInitialData()
+ */
+ public Object[] getInitialData() {
+ if( initialData == null ) {
+ initialData = getDefaultInitialData();
+ }
+ return initialData;
+ }
+
+ /**
+ * Get the base event list for the table model. This can be used to build
layered
+ * event models for filtering.
+ *
+ * @return base event list
+ */
+ public EventList getBaseEventList() {
+ if( baseList == null ) {
+ // Construct on demand
+ Object[] data = getInitialData();
+
+ if( _logger.isInfoEnabled() ) {
+ _logger.info("Table data: got " + data.length + " entries");
+ }
+
+ // Construct the event list of all our data and layer on the
sorting
+ EventList rawList = new BasicEventList();
+ rawList.addAll(Arrays.asList(data));
+ String sortProperty =
getColumnPropertyNames()[getInitialSortColumn()];
+ baseList = new SortedList(rawList, new
PropertyComparator(sortProperty, false, true));
+ }
+ return baseList;
+ }
+
+ /**
+ * Set the event list to be used for constructing the table model. The
event list
+ * provided MUST have been constructed from the list returned by
+ * [EMAIL PROTECTED] #getBaseEventList()} or this table will not work
properly.
+ *
+ * @param event list to use
+ */
+ public void setFinalEventList( EventList finalEventList ) {
+ this.finalEventList = finalEventList;
+ }
+
+ /**
+ * Get the event list to be use for constructing the table model.
+ *
+ * @return final event list
+ */
+ public EventList getFinalEventList() {
+ if( finalEventList == null ) {
+ finalEventList = getBaseEventList();
+ }
+ return finalEventList;
+ }
+
+ /**
+ * Get the table control.
+ *
+ * @return JTable instance all configured and ready to go
+ */
+ public JTable getTable() {
+ if( table == null ) {
+ createTable();
+ }
+ return table;
+ }
+
+ /**
+ * Create our control.
+ */
+ public void createTable() {
+
+ // Contstruct the table model and table to display the data
+ EventList finalEventList = getFinalEventList();
+ model = createTableModel(finalEventList);
+
+ table = new JTable(model);
+ table.setSelectionModel(new EventSelectionModel(finalEventList));
+ table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+
+ // Install the sorter
+ TableComparatorChooser tableSorter = new TableComparatorChooser(table,
baseList, true);
+
+ // Allow the derived type to configure the table
+ configureTable(table);
+
+ // Sort on the last name by default
+ tableSorter.clearComparator();
+ tableSorter.appendComparator(getInitialSortColumn(), 0, false);
+
+ // Add the context menu listener
+ table.addMouseListener(new PopupMenuMouseListener() {
+ protected JPopupMenu getPopupMenu() {
+ return createPopupContextMenu();
+ }
+ });
+
+ // Add our mouse handlers to setup our desired selection mechanics
+ table.addMouseListener(new MouseAdapter() {
+ public void mousePressed( MouseEvent e ) {
+ // If the user right clicks on a row other than the selection,
+ // then move
+ // the selection to the current row
+ int rowUnderMouse = table.rowAtPoint(e.getPoint());
+ if( e.getButton() == MouseEvent.BUTTON3 &&
!table.isRowSelected(rowUnderMouse) ) {
+ // Select the row under the mouse
+ if( rowUnderMouse != -1 ) {
+
table.getSelectionModel().setSelectionInterval(rowUnderMouse, rowUnderMouse);
+ }
+ }
+ }
+
+ /**
+ * Handle double click.
+ */
+ public void mouseClicked( MouseEvent e ) {
+ // If the user double clicked on a row, then open the
properties
+ if( e.getClickCount() == 2 ) {
+ onDoubleClick();
+ }
+ }
+ });
+
+ // Keep our status line up to date with the selections and filtering
+ getSelectionModel().addListSelectionListener(new
ListSelectionListener() {
+ public void valueChanged( ListSelectionEvent e ) {
+ updateStatusBar();
+ }
+ });
+
+ getFinalEventList().addListEventListener(this);
+ }
+
+ /**
+ * Handle a double click on a row of the table. The row will already be
selected.
+ */
+ protected void onDoubleClick() {
+ // Dispatch this to the doubleClickHandler, if any
+ if( doubleClickHandler != null ) {
+ boolean okToExecute = true;
+ if( doubleClickHandler instanceof GuardedActionCommandExecutor ) {
+ okToExecute = ((GuardedActionCommandExecutor)
doubleClickHandler).isEnabled();
+ }
+
+ if( okToExecute ) {
+ doubleClickHandler.execute();
+ }
+ }
+ }
+
+ /**
+ * Construct the table model for this table. The default implementation of
this
+ * creates a GlazedTableModel using an Advanced format.
+ *
+ * @param eventList on which to build the model
+ * @return table model
+ */
+ protected GlazedTableModel createTableModel( EventList eventList ) {
+ return new GlazedTableModel(eventList, getMessageSource(),
getColumnPropertyNames()) {
+ protected TableFormat createTableFormat() {
+ return new DefaultAdvancedTableFormat();
+ }
+ };
+ }
+
+ /**
+ * Get the data model for the table.
+ *
+ * @return model
+ */
+ public GlazedTableModel getTableModel() {
+ return model;
+ }
+
+ /**
+ * Get the names of the properties to display in the table columns.
+ *
+ * @return array of columnproperty names
+ */
+ public String[] getColumnPropertyNames() {
+ return columnPropertyNames;
+ }
+
+ /**
+ * Set the names of the properties to display in the table columns.
+ *
+ * @param columnPropertyNames
+ */
+ public void setColumnPropertyNames( String[] columnPropertyNames ) {
+ this.columnPropertyNames = columnPropertyNames;
+ }
+
+ /**
+ * @return the doubleClickHandler
+ */
+ public ActionCommandExecutor getDoubleClickHandler() {
+ return doubleClickHandler;
+ }
+
+ /**
+ * Set the handler (action executor) that should be invoked when a row in
the table is
+ * double-clicked.
+ *
+ * @param doubleClickHandler the doubleClickHandler to set
+ */
+ public void setDoubleClickHandler( ActionCommandExecutor
doubleClickHandler ) {
+ this.doubleClickHandler = doubleClickHandler;
+ }
+
+ /**
+ * @return the popupCommandGroup
+ */
+ public CommandGroup getPopupCommandGroup() {
+ return popupCommandGroup;
+ }
+
+ /**
+ * Set the command group that should be used to construct the popup menu
when a user
+ * initiates the UI gesture to show the context menu. If this is null,
then no popup
+ * menu will be shown.
+ *
+ * @param popupCommandGroup the popupCommandGroup to set
+ */
+ public void setPopupCommandGroup( CommandGroup popupCommandGroup ) {
+ this.popupCommandGroup = popupCommandGroup;
+ }
+
+ /**
+ * Configure the newly created table as needed. Install any needed column
sizes,
+ * renderers, and comparators. The default implementation does nothing.
+ *
+ * @param table The table to configure
+ */
+ protected void configureTable( JTable table ) {
+ }
+
+ /**
+ * Get the default set of objects for this table.
+ *
+ * @return Array of data for the table
+ */
+ protected abstract Object[] getDefaultInitialData();
+
+ /**
+ * Determine if the event should be handled on this table. If
<code>true</code> is
+ * returned (the default), then the list holding the table data will be
scanned for
+ * the object and updated appropriately depending on then event type.
+ *
+ * @param event to inspect
+ * @return boolean true if the object should be handled, false otherwise
+ * @see #handleDeletedObject(Object)
+ * @see #handleNewObject(Object)
+ * @see #handleUpdatedObject(Object)
+ *
+ */
+ protected boolean shouldHandleEvent( ApplicationEvent event ) {
+ return true;
+ }
+
+ /**
+ * Create the context popup menu, if any, for this table. The default
operation is to
+ * create the popup from the command group if one has been specified. If
not, then
+ * null is returned.
+ *
+ * @return popup menu to show, or null if none
+ */
+ protected JPopupMenu createPopupContextMenu() {
+ return (getPopupCommandGroup() != null) ?
getPopupCommandGroup().createPopupMenu() : null;
+ }
+
+ /**
+ * Get the default sort column. Defaults to 0.
+ *
+ * @return column to sort on
+ */
+ protected int getInitialSortColumn() {
+ return 0;
+ }
+
+ /**
+ * Get the selection model.
+ *
+ * @return selection model
+ */
+ public ListSelectionModel getSelectionModel() {
+ return table.getSelectionModel();
+ }
+
+ /**
+ * Handle the creation of a new object.
+ *
+ * @param object New object to handle
+ */
+ protected void handleNewObject( Object object ) {
+ getFinalEventList().add(object);
+ }
+
+ /**
+ * Handle an updated object in this table. Locate the existing entry (by
equals) and
+ * replace it in the underlying list.
+ *
+ * @param object Updated object to handle
+ */
+ protected void handleUpdatedObject( Object object ) {
+ int index = baseList.indexOf(object);
+ if( index >= 0 ) {
+ baseList.set(index, object);
+ }
+ }
+
+ /**
+ * Handle the deletion of an object in this table. Locate this entry (by
equals) and
+ * delete it.
+ *
+ * @param object Updated object being deleted
+ */
+ protected void handleDeletedObject( Object object ) {
+ int index = baseList.indexOf(object);
+ if( index >= 0 ) {
+ baseList.remove(index);
+ }
+ }
+
+ /**
+ * Set the status bar associated with this table. If non-null, then any
time the final
+ * event list on this table changes, then the status bar will be updated
with the
+ * current object counts.
+ *
+ * @param statusBar to update
+ */
+ public void setStatusBar( StatusBarCommandGroup statusBar ) {
+ this.statusBar = statusBar;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
ca.odell.glazedlists.event.ListEventListener#listChanged(ca.odell.glazedlists.event.ListEvent)
+ */
+ public void listChanged( ListEvent listChanges ) {
+ // Our object list has changed, so update the status bar
+ updateStatusBar();
+ }
+
+ /**
+ * Update the status bar with the current display counts.
+ */
+ protected void updateStatusBar() {
+ if( statusBar != null ) {
+ int all = getBaseEventList().size();
+ int showing = getFinalEventList().size();
+ String msg;
+ if( all == showing ) {
+ String[] keys = new String[] { modelId + "." +
SHOWINGALL_MSG_KEY, SHOWINGALL_MSG_KEY };
+ msg = getMessage(keys, new Object[] { ""+all, (all != 1) ?
objectPluralName : objectSingularName } );
+ } else {
+ String[] keys = new String[] { modelId + "." +
SHOWINGN_MSG_KEY, SHOWINGN_MSG_KEY };
+
+ msg = getMessage(keys, new Object[] { ""+showing, (showing !=
1) ? objectPluralName : objectSingularName, ""+all } );
+ }
+
+ // Now add the selection info
+ int nselected = table.getSelectedRowCount();
+ if( nselected > 0 ) {
+ String[] keys = new String[] { modelId + "." +
SELECTEDN_MSG_KEY, SELECTEDN_MSG_KEY };
+
+ msg += getMessage(keys, new Object[] { ""+nselected } );
+ }
+
+ statusBar.setMessage(msg.toString());
+ }
+ }
+
+ /**
+ * Set the event redispatcher. This will be configured via the application
context.
+ * When this is set, we add our handler so that we will receive proper
application
+ * event notification.
+ *
+ * @param dispatcher
+ */
+ public void setApplicationEventRedispatcher( ApplicationEventRedispatcher
dispatcher ) {
+ dispatcher.addListener(new ApplicationEventHandler());
+ }
+
+ /**
+ * This class will handle the application event dispatching. It might seem
correct to
+ * just have the main class implement ApplicationListener, but that will
lead to
+ * problems since instances of this class are commonly created in the
application
+ * context as prototypes (non-singletons) which leads to an extra instance
being
+ * created that is not fully initialized.
+ * <p>
+ * This stems from the way that the ApplicationListener tag interface is
handled
+ * within the application context. After all the bean definitions have been
+ * internalized, the context looks for any beans that implement the
+ * ApplicationListener interface. It then obtains the bean and registers
it as a
+ * listener. Since object tables are typically prototypes, this creates an
instance of
+ * the table that is not tied into any other object and has not been
completely
+ * initialized.
+ * <p>
+ * So, to avoid all that, we simply use an internal class to handle the
events.
+ */
+ private class ApplicationEventHandler implements ApplicationListener {
+ /**
+ * Handle an application event. This will notify us of object adds,
deletes, and
+ * modifications. Update our table model accordingly.
+ *
+ * @param e event to process
+ */
+ public void onApplicationEvent( ApplicationEvent e ) {
+ if( e instanceof LifecycleApplicationEvent ) {
+ LifecycleApplicationEvent le = (LifecycleApplicationEvent) e;
+ if( shouldHandleEvent(e) ) {
+ if( le.getEventType() == LifecycleApplicationEvent.CREATED
) {
+ handleNewObject(le.getObject());
+ } else if( le.getEventType() ==
LifecycleApplicationEvent.MODIFIED ) {
+ handleUpdatedObject(le.getObject());
+ } else if( le.getEventType() ==
LifecycleApplicationEvent.DELETED ) {
+ handleDeletedObject(le.getObject());
+ }
+ }
+ }
+ }
+ }
+}
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
spring-rich-c-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/spring-rich-c-cvs