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 }