001 /*
002 * Copyright 2002-2005 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package org.springframework.richclient.form;
017
018 import java.beans.PropertyChangeListener;
019 import java.beans.PropertyChangeSupport;
020
021 import javax.swing.JComponent;
022
023 import org.springframework.binding.form.FormModel;
024 import org.springframework.binding.form.HierarchicalFormModel;
025 import org.springframework.binding.value.PropertyChangePublisher;
026 import org.springframework.binding.value.ValueModel;
027 import org.springframework.binding.value.support.ObservableList;
028 import org.springframework.binding.value.support.ValueHolder;
029 import org.springframework.richclient.command.AbstractCommand;
030 import org.springframework.richclient.command.ActionCommand;
031 import org.springframework.richclient.command.CommandGroup;
032 import org.springframework.richclient.util.GuiStandardUtils;
033 import org.springframework.util.Assert;
034 import org.springframework.util.StringUtils;
035
036 /**
037 * This is an abstract base implementation of the detail side of a Master/Detail form
038 * pair. Derived types need only implement {@link AbstractForm#createFormControl()}.
039 * <p>
040 * The various form commands can be secured by specifying security controller id's for
041 * the command. Use the {@link AbstractForm#getCommitSecurityControllerId()} and
042 * {@link AbstractForm#getNewFormObjectCommand()}.
043 *
044 * @author Larry Streepy
045 *
046 */
047 public abstract class AbstractDetailForm extends AbstractForm implements PropertyChangePublisher {
048
049 /** State indicating that we are editing no object. */
050 public static final int STATE_CLEAR = 0;
051
052 /** State indicating that we are editing an existing object. */
053 public static final int STATE_EDIT = 1;
054
055 /** State indicating that we are creating a new object. */
056 public static final int STATE_CREATE = 2;
057
058 /** Edit state property name for change notifications. */
059 public static final String EDIT_STATE_PROPERTY = "edit_state";
060
061 /**
062 * @param pageFormModel
063 */
064 protected AbstractDetailForm(FormModel formModel, String formId, ObservableList editableItemList) {
065 super( formModel, formId );
066
067 // Install the detail data as our editable object list
068 this.editableItemList = editableItemList;
069 setEditableFormObjects( editableItemList );
070 setEditingFormObjectIndexHolder(indexHolder);
071 }
072
073 /**
074 * Construct a detail form using the provided parent form model (we will construct our
075 * own form model as a child of the parent model). The provided masterList will be
076 * installed as the set of editable form objects.
077 *
078 * @param parentFormModel
079 * @param formId
080 * @param childFormObjectHolder
081 * @param masterList ObservableList holding the editable items
082 */
083 public AbstractDetailForm(HierarchicalFormModel parentFormModel, String formId, ValueModel childFormObjectHolder,
084 ObservableList masterList) {
085 super( parentFormModel, formId, childFormObjectHolder );
086 setMasterList( masterList );
087 setEditingFormObjectIndexHolder(indexHolder);
088 }
089
090 /**
091 * Set the master list model.
092 *
093 * @param masterList list to use as our master data
094 */
095 protected void setMasterList(ObservableList masterList) {
096 editableItemList = masterList;
097 setEditableFormObjects(editableItemList);
098 }
099
100 /**
101 * Set the selected object index.
102 *
103 * @param index of selected item
104 */
105 public void setSelectedIndex(int index) {
106 indexHolder.setValue( new Integer( index ) );
107 setEditState( index < 0 ? STATE_CLEAR : STATE_EDIT );
108 updateControlsForState();
109 }
110
111 /**
112 * @return index of item being edited
113 */
114 public int getSelectedIndex() {
115 return getEditingFormObjectIndex();
116 }
117
118 /**
119 * Get the value holder containing the editing index. This allows triggers to monitor
120 * for changes in the index of the object we are editing.
121 *
122 * @return
123 */
124 public ValueHolder getEditingIndexHolder() {
125 return indexHolder;
126 }
127
128 /**
129 * Set the form for "create new object" mode. This will set controls as needed for
130 * this edit mode.
131 */
132 public void creatingNewObject() {
133 setEditState( STATE_CREATE );
134 updateControlsForState();
135 }
136
137 /**
138 * Update our controls based on our state.
139 */
140 protected void updateControlsForState() {
141 boolean showCancel = false;
142 boolean showRevert = false;
143
144 switch( getEditState() ) {
145 case STATE_CREATE:
146 showCancel = true;
147 showRevert = false;
148 break;
149 case STATE_CLEAR:
150 showCancel = false;
151 showRevert = false;
152 break;
153
154 case STATE_EDIT:
155 showCancel = false;
156 showRevert = true;
157 break;
158 default:
159 Assert.isTrue( false, "Invalid edit state: " + getEditState() );
160 }
161
162 getCancelCommand().setVisible( showCancel );
163 getRevertCommand().setVisible( showRevert );
164 }
165
166 /**
167 * Set the current edit state.
168 *
169 * @param new edit state
170 */
171 protected void setEditState(int editState) {
172 int oldEditState = this.editState;
173 this.editState = editState;
174 updateControlsForState();
175 firePropertyChange( EDIT_STATE_PROPERTY, oldEditState, this.editState);
176 }
177
178 /**
179 * Get the current edit state: one of {@link #STATE_CLEAR}, {@link #STATE_CREATE},
180 * or {@link #STATE_EDIT}.
181 *
182 * @return current state
183 */
184 public int getEditState() {
185 return editState;
186 }
187
188 /**
189 * Commit this forms data back to the master table. Let our super class do all the
190 * work and then just inform our master table that the value has changed.
191 */
192 public void postCommit(FormModel formModel) {
193 super.postCommit( formModel );
194
195 // Now set the selected index back to -1 so that the forms properly reset
196 setSelectedIndex( -1 );
197 }
198
199 protected String getRevertCommandFaceDescriptorId() {
200 return "revert";
201 }
202
203 protected String getCommitCommandFaceDescriptorId() {
204 return "save";
205 }
206
207 protected String getCancelCommandFaceDescriptorId() {
208 return "cancelNew";
209 }
210
211 /**
212 * Override to return null for the new object security controller id. We do
213 * this because this command is not used directly, so it shouldn't be controlled.
214 * The {@link AbstractMasterForm} is responsible for the real (invocable)
215 * instance of this command.
216 *
217 * @return null
218 */
219 protected String getNewFormObjectSecurityControllerId() {
220 return null;
221 }
222
223 /**
224 * Return the configured cancel command, creating it if necessary.
225 *
226 * @return cancel command
227 */
228 public ActionCommand getCancelCommand() {
229 if( cancelCommand == null ) {
230 cancelCommand = createCancelCommand();
231 }
232 return cancelCommand;
233 }
234
235 /**
236 * Create the cancel command. This will cancel the "create new" operation and reset
237 * the form.
238 *
239 * @return cancel command action
240 */
241 protected ActionCommand createCancelCommand() {
242 String commandId = getCancelCommandFaceDescriptorId();
243 if( !StringUtils.hasText( commandId ) ) {
244 return null;
245 }
246 ActionCommand command = new ActionCommand( commandId ) {
247 protected void doExecuteCommand() {
248 AbstractDetailForm.this.reset();
249 AbstractDetailForm.this.setEnabled( false );
250 setEditingNewFormObject( false );
251 setEditingFormObjectIndexSilently( -1 );
252 setEditState( STATE_CLEAR );
253 setFormObject( null );
254 }
255 };
256 return (ActionCommand) getCommandConfigurer().configure( command );
257 }
258
259 /**
260 * Return a standardized row of command buttons, right-justified and all of the same
261 * size, with OK as the default button, and no mnemonics used, as per the Java Look
262 * and Feel guidelines.
263 */
264 protected JComponent createButtonBar() {
265 commitCommand = getCommitCommand();
266 revertCommand = getRevertCommand();
267 cancelCommand = getCancelCommand();
268
269 formCommandGroup = CommandGroup.createCommandGroup( null, new AbstractCommand[] {cancelCommand,
270 revertCommand, commitCommand} );
271 JComponent buttonBar = formCommandGroup.createButtonBar();
272 GuiStandardUtils.attachDialogBorder( buttonBar );
273 return buttonBar;
274 }
275
276 // =======================================
277 // PropertyChangePublisher implementation
278 // =======================================
279
280 public final void addPropertyChangeListener(PropertyChangeListener listener) {
281 if( listener == null ) {
282 return;
283 }
284 if( changeSupport == null ) {
285 changeSupport = new PropertyChangeSupport( this );
286 }
287 changeSupport.addPropertyChangeListener( listener );
288 }
289
290 public final void removePropertyChangeListener(PropertyChangeListener listener) {
291 if( listener == null || changeSupport == null ) {
292 return;
293 }
294 changeSupport.removePropertyChangeListener( listener );
295 }
296
297 public final void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
298 if( listener == null ) {
299 return;
300 }
301 if( changeSupport == null ) {
302 changeSupport = new PropertyChangeSupport( this );
303 }
304 changeSupport.addPropertyChangeListener( propertyName, listener );
305 }
306
307 public final void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
308 if( listener == null || changeSupport == null ) {
309 return;
310 }
311 changeSupport.removePropertyChangeListener( propertyName, listener );
312 }
313
314 protected final void firePropertyChange(String propertyName, int oldValue, int newValue) {
315 if( changeSupport == null ) {
316 return;
317 }
318 changeSupport.firePropertyChange( propertyName, oldValue, newValue );
319 }
320
321 private ValueHolder indexHolder = new ValueHolder( new Integer( -1 ) );
322 private CommandGroup formCommandGroup;
323 private ActionCommand commitCommand;
324 private ActionCommand revertCommand;
325 private ActionCommand cancelCommand;
326 private ObservableList editableItemList;
327 private int editState = STATE_CLEAR;
328 private transient PropertyChangeSupport changeSupport;
329
330 }