001: package net.xoetrope.xui.data;
002:
003: import java.util.Hashtable;
004:
005: import java.awt.Component;
006:
007: import net.xoetrope.xui.XListHolder;
008: import net.xoetrope.xui.XProjectManager;
009:
010: /**
011: * <p>Bind a list to a data model value/node. The binding allows a list model node to
012: * linked to a UI component so that it can be refreshed when new data is written
013: * to the model or conversely when the UI component needs to write data to the
014: * model.<br>This binding is designed to be used by list like components such
015: * as comboboxes or drop down lists.</p>
016: * <p>Copyright: Copyright (c) Xoetrope Ltd., 1998-2003<br>
017: * License: see license.txt
018: * @version $Revision: 1.29 $
019: */
020: public class XListBinding implements XDataBinding {
021: protected XListHolder comp;
022: protected XListModelAdapter model;
023: protected XModel outputNode;
024: protected boolean useUnique;
025: protected boolean bDirty;
026: protected boolean saveToSourceNode;
027: protected String srcPath;
028: protected String outputPath;
029:
030: public XListBinding() {
031: }
032:
033: /**
034: * Construct a new data binding
035: * @param c the component to be bound
036: * @param dataElement the name of the data in the model
037: */
038: public XListBinding(Component c, String dataElement) {
039: this (c, dataElement, null, null, null);
040: }
041:
042: /**
043: * Construct a new data binding
044: * @param c the component to be bound
045: * @param dataElement the name of the data in the model
046: * @param srcModel the model node that acts as the data source for this node
047: */
048: public XListBinding(Component c, String dataElement, XModel srcModel) {
049: this (c, dataElement, null, srcModel, null);
050: }
051:
052: /**
053: * Construct a new data binding
054: * @param c the component to be bound
055: * @param dataElement the name of the data in the model
056: * @param destElement the name of the destination data in the model
057: */
058: public XListBinding(Component c, String dataElement,
059: String destElement) {
060: this (c, dataElement, destElement, null, null, true);
061: }
062:
063: /**
064: * Construct a new data binding
065: * @param c the component to be bound
066: * @param dataElement the name of the data in the model
067: * @param destElement the name of the destination data in the model
068: * @param srcModel the model node that acts as the data source for this node
069: * @param dstModel the model node to which the selection is saved
070: */
071: public XListBinding(Component c, String dataElement,
072: String destElement, XModel srcModel, XModel dstModel) {
073: this (c, dataElement, destElement, srcModel, dstModel, true);
074: }
075:
076: /**
077: * Construct a new data binding
078: * Set the languageResourceBundle for text to be translated
079: * @param c the component to be bound
080: * @param dataElement the name of the data in the model
081: * @param destElement the name of the destination data in the model
082: * @param srcModel the model node that acts as the data source for this node
083: * @param dstModel the model node to which the selection is saved
084: * @param saveToSource true to save the selected value as the value of the source node, false to save only to the output node
085: */
086: public XListBinding(Component c, String dataElement,
087: String destElement, XModel srcModel, XModel dstModel,
088: boolean saveToSource) {
089: srcPath = dataElement;
090: outputPath = XModel.prefixOutputPath(destElement);
091: saveToSourceNode = saveToSource;
092: if (destElement == null)
093: outputPath = XModel.prefixOutputPath(dataElement);
094: outputNode = dstModel;
095: if ((dstModel == null) && (destElement != null))
096: outputNode = (XModel) XProjectManager.getModel().get(
097: outputPath);
098: if ((srcModel == null) && (dataElement != null))
099: srcModel = (XModel) XProjectManager.getModel().get(
100: dataElement);
101: model = new XListModelAdapter(srcModel);
102: comp = (XListHolder) c;
103: useUnique = false;
104: bDirty = true;
105: }
106:
107: /**
108: * Construct a new data binding
109: * @param c the component to be bound
110: * @param node the model node
111: */
112: public XListBinding(Component c, XModel node) {
113: model = new XListModelAdapter(node);
114: comp = (XListHolder) c;
115: }
116:
117: /**
118: * Construct a new data binding
119: * @param c the component to be bound
120: * @param node the model node
121: */
122: public XListBinding(Component c, XModelAdapter node) {
123: model = (XListModelAdapter) node;
124: comp = (XListHolder) c;
125: }
126:
127: /**
128: * Updates the TextComponent with the value obtained from the data model.
129: * Filters the list by ignoring repeat items (note that this does not
130: * guarantee unique list entries unless the useUnique flag is set).
131: */
132: public void get() {
133: Hashtable hashTable = null;
134: if (useUnique)
135: hashTable = new Hashtable();
136: String lastItem = "";
137: Object selectedObj = null;
138: if (outputNode != null)
139: selectedObj = outputNode.get();
140: else
141: selectedObj = comp.getSelectedObject();
142:
143: if (selectedObj == null && comp.getItemCount() > 0)
144: comp.select(0); // reset in the case of dynamic binding
145:
146: int items = comp.getItemCount();
147: int numChildren = model.getNumChildren();
148: if (bDirty || (items != numChildren)) { // This should really check a 'dirty' flag
149: bDirty = false;
150: comp.removeAll();
151: String selected = null;
152: if (selectedObj != null)
153: selected = selectedObj.toString();
154:
155: int newItemIdx = 0;
156: for (int i = 0; i < numChildren; i++) {
157: Object obj = model.get(i);
158: if (obj == null)
159: continue;
160: String currentObj = (String) obj.toString();
161: boolean addItem = false;
162: if (useUnique) {
163: if (hashTable.get(currentObj) == null) {
164: hashTable.put(currentObj, currentObj);
165: addItem = true;
166: }
167: } else if (currentObj.compareTo(lastItem) != 0)
168: addItem = true;
169:
170: if (addItem) {
171: String item = addItem((String) currentObj);
172: if (selected != null && selected.equals(item))
173: comp.select(newItemIdx);
174: lastItem = currentObj;
175: newItemIdx++;
176: }
177: }
178: } else if (selectedObj != null)
179: comp.select(selectedObj); // The list is not being populated so set the value
180:
181: set(); // in the case of dynamic bindings we want to update the model with what is seleted by default.
182: }
183:
184: /**
185: * Add an item to the component. In a separate method to aid with subclassing
186: * for localisation
187: * @param s
188: */
189: protected String addItem(String s) {
190: comp.addItem(s);
191: return s;
192: }
193:
194: /**
195: * Updates the data model with the value retrieved from the TextComponent.
196: */
197: public void set() {
198: Object selObj = comp.getSelectedObject();
199: if (saveToSourceNode)
200: model.set(selObj);
201: if (outputNode != null)
202: outputNode.set(selObj);
203: }
204:
205: /**
206: * Set the list to use unique entries (remove duplicates)
207: * @param isUnique teh new flag value
208: */
209: public void setUseUnique(boolean isUnique) {
210: useUnique = isUnique;
211: }
212:
213: /**
214: * Gets the component on which this binding operates
215: * @return the bound component
216: */
217: public Component getComponent() {
218: return (Component) comp;
219: }
220:
221: /**
222: * Gets the name of the model node
223: * @return the name
224: */
225: public String getName() {
226: return ((XListModelAdapter) model).getTagName();
227: }
228:
229: /**
230: * Get the model component to which this object binds
231: * @return
232: */
233: public String getSourcePath() {
234: return srcPath;
235: }
236:
237: /**
238: * Set the model path for the source data
239: */
240: public void setSourcePath(String newPath) {
241: srcPath = newPath;
242: }
243:
244: /**
245: * Set the model path for the output/state data
246: */
247: public void setOutputPath(String newPath) {
248: outputPath = XModel.prefixOutputPath(newPath);
249: }
250:
251: /**
252: * Get the model component to which this object binds
253: * @return
254: */
255: public String getOutputPath() {
256: return outputPath;
257: }
258:
259: /**
260: * Set the source for this bindings's data
261: * @param newNode the path to the data in the model
262: */
263: public void setSource(XModel newNode) {
264: if (model.getModel() != newNode) {
265: model.setModel(newNode);
266: bDirty = true;
267: get();
268: }
269: }
270:
271: /**
272: * Set the output node where state data for this binding will be saved
273: * @param newPath the path of the output/state node
274: */
275: public void setOutput(XModel newNode) {
276: if ((outputNode == null) || !outputNode.equals(newNode)) {
277: outputNode = newNode;
278: if ((outputNode.get() == null) && (comp.getItemCount() > 0)) {
279: // Try selecting the previous selection
280: try {
281: Object sel = comp.getSelectedObject();
282: comp.select(sel);
283: if (!comp.getSelectedObject().equals(sel))
284: comp.select(0);
285: } catch (Exception e) {
286: comp.select(0);
287: }
288: }
289: }
290: }
291: }
|