SettingsModelListener

Changing a component depending on the value of another one:

You can tie components together with listeners to the SettingsModel. You can change the value of a component or en-/disable it depending on the value of another. You always go through the SettingsModels to do that. If a SettingsModel is disabled the corresponding component is disabled too (in the dialog). The SettingsModel stores the enable status and if the workflow is loaded and the dialog is opened again, the enable status is restored. Lets look at the following example:

This is the dialog we are creating. It has a selection box with three choices. Depending on the selection we want to set a value in the parameter field. The parameter is only user editable, if the corresponding box is checked. Otherwise the editfield is disabled.

Creating the dialog:

We need three SettingsModels to store the three values. In the NodeModel (we named the class TestNodeModel,) we create factory methods:

  	  static SettingsModelString createSettingsModelSelection() {          return new SettingsModelString("SELECTION", "");      }         static SettingsModelInteger createSettingsModelValue() {          return new SettingsModelInteger("VALUE", 0);      }      static SettingsModelBoolean createSettingsModelEnabled() {          return new SettingsModelBoolean("ENABLE", false);      }  

In the NodeDialog's constructor we instantiate the three settings models and assign them to local variables because we need to register listeners with them later on:

  SettingsModelString selection = TestNodeModel.createSettingsModelSelection();  SettingsModelBoolean enabled = TestNodeModel.createSettingsModelEnabled();  SettingsModelInteger parameter = TestNodeModel.createSettingsModelValue();  

We use these settings models with a StringSelection component (a combo box), a boolean component (a check box), and with a NumberEdit component (a text field to enter numbers). We create two groups and also change the orientation to arrange them nicely:

  createNewGroup("Your choice");  addDialogComponent(new DialogComponentStringSelection(      selection, "Select one:", TestNodeModel.SELECTION));  createNewGroup("Parameter");  setHorizontalPlacement(true);  addDialogComponent(new DialogComponentBoolean(enabled, "edit manually"));  addDialogComponent(new DialogComponentNumberEdit(parameter, "", 15));  

Adding listeners to the SettingsModel:

Now, in order to enable the parameter edit field if (and only if) the checkbox is marked, add a listener to the boolean settings model:

  enabled.addChangeListener(new ChangeListener() {      public void stateChanged(final ChangeEvent e) {          // if enabled is true, the parameter field should be enabled          parameter.setEnabled(enabled.getBooleanValue());      }  });  

When notified (i.e. when the user changes the checkbox) it reads the value from the boolean settings model and enables the parameter settings model accordingly. Whenever the settings model changes (the value or the enable status) it is being forwarded to the dialog component. You can not only change the enable status of a component in a listener - you can also change another component's value depending on the value of the notifier, for example:

     selection.addChangeListener(new ChangeListener() {      public void stateChanged(final ChangeEvent e) {          int param = selection.getStringValue().hashCode();          // we override any previously entered value!          parameter.setIntValue(param);      }  });  

Whenever the selection changes, this listener is called and sets a new parameter value depending on the current selection. (To make the code simpler, we just set the hash code...which fails, if the string value is null, btw.)

Important Note:

There is a very important detail, that, if you forget it, will cause the components to appear in the dialog in an "invalid" state. In the example, the edit field might be enabled even though the checkbox is not marked. In short, you must maintain dependencies at all times. That is, whenever a value is set in the settings model and no listener is (yet) installed, the enable status or value of the settings model that should be tied to the first one must be adapted. In the example, that is the case at construction time of the settings models. We instantiate the settings model for the boolean value with the initial value false. Thus, the parameter settings model should be disabled at the same time. If you run the example with the code from above and open the dialog for the first time, you will notice that the parameter edit field is enabled, even though the check mark is not set. Only after you changed the checkbox will it maintain the correct status dependency. To fix this, we can either initialize the settings model in a consistent way (e.g. init the enable model with true or disable the parameter value model in its factory method,) or set the correct states when we use them (in the constructors.) It is also very important to do this in the NodeModel! If you are not planning on modifying the values in your node model (i.e. if you just want to read user settings from them,) then there is no real need to install listeners in the settings models instances used in your NodeModel. But you need to make sure that they are initialized consistently. When you open the dialog for the first time, if you find that your component is enabled, even though the check mark that should disable it is set, then your settings in the NodeModel probably don't reflect the dependencies in the correct way. (Even though the values are loaded into the node dialog each time it opens, you must maintain this. You may expect that if the values are loaded the listeners will take care of setting the correct status, which is true, but the order in which the values are loaded is not defined. If it happens that the value determining the status of another component is loaded before the (wrong) status of the dependant is loaded, it can overload the correct status.)