001: /*
002: #IFNDEF ALT_LICENSE
003: ThinWire(R) RIA Ajax Framework
004: Copyright (C) 2003-2007 Custom Credit Systems
005:
006: This library is free software; you can redistribute it and/or modify it under
007: the terms of the GNU Lesser General Public License as published by the Free
008: Software Foundation; either version 2.1 of the License, or (at your option) any
009: later version.
010:
011: This library is distributed in the hope that it will be useful, but WITHOUT ANY
012: WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
013: PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
014:
015: You should have received a copy of the GNU Lesser General Public License along
016: with this library; if not, write to the Free Software Foundation, Inc., 59
017: Temple Place, Suite 330, Boston, MA 02111-1307 USA
018:
019: Users who would rather have a commercial license, warranty or support should
020: contact the following company who invented, built and supports the technology:
021:
022: Custom Credit Systems, Richardson, TX 75081, USA.
023: email: info@thinwire.com ph: +1 (888) 644-6405
024: http://www.thinwire.com
025: #ENDIF
026: [ v1.2_RC2 ]
027: */
028: package thinwire.ui;
029:
030: import java.util.List;
031:
032: import thinwire.ui.GridBox.Row;
033: import thinwire.ui.event.ActionEvent;
034: import thinwire.ui.event.ActionListener;
035: import thinwire.ui.event.ItemChangeEvent;
036: import thinwire.ui.event.ItemChangeListener;
037: import thinwire.ui.event.PropertyChangeEvent;
038: import thinwire.ui.event.PropertyChangeListener;
039:
040: /**
041: * A DropDownGridBox wraps around a GridBox component to provide drop down
042: * features.
043: * <p>
044: * <b>Example:</b> <br>
045: * <img src="doc-files/DropDownGridBox-1.png"> <br>
046: *
047: * <pre>
048: * Dialog dlg = new Dialog("DropDownGridBox Test");
049: * dlg.setBounds(25, 25, 400, 200);
050: *
051: * final TextField tf = new TextField();
052: * tf.setBounds(275, 25, 100, 20);
053: * dlg.getChildren().add(tf);
054: *
055: * DropDownGridBox ddgb = new DropDownGridBox();
056: * ddgb.setBounds(25, 25, 230, 20);
057: *
058: * GridBox gb = ddgb.getComponent();
059: * gb.setVisibleHeader(true);
060: * gb.setHeight(120);
061: *
062: * GridBox.Column col1 = new GridBox.Column();
063: * col1.setName("Name");
064: * GridBox.Column col2 = new GridBox.Column();
065: * col2.setName("City");
066: * GridBox.Column col3 = new GridBox.Column();
067: * col3.setName("Country");
068: * gb.getColumns().add(col1);
069: * gb.getColumns().add(col2);
070: * gb.getColumns().add(col3);
071: *
072: * String[] names = { "Smythe", "Janes", "Warren", "Dempster", "Hilcox" };
073: * String[] cities = { "Tokyo", "Hong Kong", "Lethbridge", "Juarez", "Juneau" };
074: * String[] countries = { "Japan", "China", "Canada", "Mexico", "USA" };
075: *
076: * for (int r = 0; r < 5; r++) {
077: * GridBox.Row row = new GridBox.Row();
078: * row.add(names[r]);
079: * row.add(cities[r]);
080: * row.add(countries[r]);
081: * gb.getRows().add(row);
082: * }
083: *
084: * ((DropDownGridBox.DefaultView) ddgb.getView()).setColumnIndex(2);
085: *
086: * ddgb.addPropertyChangeListener(DropDownGridBox.PROPERTY_TEXT,
087: * new PropertyChangeListener() {
088: * public void propertyChange(PropertyChangeEvent evt) {
089: * tf.setText((String) evt.getNewValue());
090: * }
091: * });
092: *
093: * dlg.getChildren().add(ddgb);
094: * dlg.setVisible(true);
095: * </pre>
096: *
097: * </p>
098: * <p>
099: * <b>Keyboard Navigation:</b><br>
100: * <table border="1">
101: * <tr>
102: * <td>KEY</td>
103: * <td>RESPONSE</td>
104: * <td>NOTE</td>
105: * </tr>
106: * <tr>
107: * <td>Down Arrow</td>
108: * <td>Drops the Grid Box down.</td>
109: * <td>Only if the component has focus.</td>
110: * </tr>
111: * <tr>
112: * <td>Esc</td>
113: * <td>Closes the Grid Box</td>
114: * <td>Only if the component has focus.</td>
115: * </tr>
116: * </table> See GridBox for additional keyboard support.
117: * </p>
118: *
119: * @author Joshua J. Gertzen
120: * @author Ted C. Howard
121: */
122: public class DropDownGridBox extends DropDown<GridBox> {
123: public static class DefaultView extends
124: DropDown.AbstractView<GridBox> {
125:
126: private static final int MIN_SIZE = 25;
127: private static final int DEFAULT_MAX_WIDTH = 640 / 2 - 10;
128: private static final int DEFAULT_MAX_HEIGHT = 480 / 2 - 20;
129:
130: private int columnIndex;
131: private String delimiter;
132:
133: private PropertyChangeListener childChangePcl = new PropertyChangeListener() {
134: public void propertyChange(PropertyChangeEvent ev) {
135: if (ev.getOldValue() != null) {
136: GridBox gb = (GridBox) ev.getOldValue();
137: gb.removePropertyChangeListener(childChangePcl);
138: gb.removeActionListener(clickListener);
139: gb.removeItemChangeListener(itemChangeListener);
140: }
141: if (ev.getNewValue() != null) {
142: GridBox gb = (GridBox) ev.getNewValue();
143: gb.addPropertyChangeListener(
144: GridBox.Row.PROPERTY_ROW_CHILD,
145: childChangePcl);
146: gb.addActionListener(GridBox.ACTION_CLICK,
147: clickListener);
148: iterateRows(gb.getRows());
149: gb.addItemChangeListener(itemChangeListener);
150: }
151: }
152: };
153:
154: private PropertyChangeListener checkListener = new PropertyChangeListener() {
155: public void propertyChange(PropertyChangeEvent ev) {
156: dd.setText(getValue().toString());
157: }
158: };
159:
160: private ActionListener clickListener = new ActionListener() {
161: public void actionPerformed(ActionEvent ev) {
162: if (((GridBox.Range) ev.getSource()).getRow()
163: .getChild() == null) {
164: dd.setText(getValue().toString());
165: }
166: }
167: };
168:
169: private ItemChangeListener itemChangeListener = new ItemChangeListener() {
170: public void itemChange(ItemChangeEvent ev) {
171: ItemChangeEvent.Type type = ev.getType();
172: GridBox.Range range = (GridBox.Range) ev.getPosition();
173: int columnIndex = range.getColumnIndex();
174: int rowIndex = range.getRowIndex();
175:
176: if ((columnIndex == -1 && rowIndex >= 0)
177: && (type == ItemChangeEvent.Type.ADD || type == ItemChangeEvent.Type.SET)) {
178: GridBox.Row row = (GridBox.Row) ev.getNewValue();
179: GridBox child = row.getChild();
180: if (child != null) {
181: child.addPropertyChangeListener(
182: GridBox.Row.PROPERTY_ROW_CHILD,
183: childChangePcl);
184: child.addActionListener(GridBox.ACTION_CLICK,
185: clickListener);
186: iterateRows(child.getRows());
187: child.addItemChangeListener(itemChangeListener);
188: }
189: }
190: }
191: };
192:
193: DefaultView() {
194: setColumnIndex(0);
195: setDelimiter(",");
196: }
197:
198: @Override
199: protected void init(DropDown<GridBox> ddgb, GridBox gb) {
200: super .init(ddgb, gb);
201: if (dd != null) {
202: ddc.addPropertyChangeListener(
203: GridBox.Row.PROPERTY_ROW_CHILD, childChangePcl);
204: ddc.addActionListener(GridBox.ACTION_CLICK,
205: clickListener);
206: ddc
207: .addPropertyChangeListener(
208: GridBox.Row.PROPERTY_ROW_CHECKED,
209: checkListener);
210: ddc.addItemChangeListener(itemChangeListener);
211: iterateRows(ddc.getRows());
212: }
213: }
214:
215: void iterateRows(List<GridBox.Row> rows) {
216: for (GridBox.Row r : rows) {
217: GridBox child = r.getChild();
218: if (child != null) {
219: child.addPropertyChangeListener(
220: GridBox.Row.PROPERTY_ROW_CHILD,
221: childChangePcl);
222: child.addActionListener(GridBox.ACTION_CLICK,
223: clickListener);
224: iterateRows(child.getRows());
225: }
226: }
227: }
228:
229: public DropDownGridBox getDropDown() {
230: return (DropDownGridBox) dd;
231: }
232:
233: GridBox getGridBox() {
234: GridBox gb = dd == null ? ddc : dd.getComponent();
235: GridBox.Row row = gb.getSelectedRow();
236:
237: while (row != null && row.getChild() != null) {
238: gb = row.getChild();
239: row = gb.getSelectedRow();
240: }
241:
242: return gb;
243: }
244:
245: public Object getValue() {
246: GridBox gb = getGridBox();
247: if (gb == null)
248: throw new IllegalStateException("gridBox == null");
249: String delimiter;
250: int columnIndex;
251: delimiter = this .delimiter;
252: columnIndex = this .columnIndex;
253:
254: if (columnIndex >= gb.getColumns().size())
255: throw new IllegalStateException(
256: "columnIndex >= gridBox.getColumns().size()");
257: String s;
258:
259: if (gb.isVisibleCheckBoxes()) {
260: StringBuilder sb = new StringBuilder();
261:
262: for (GridBox.Row r : gb.getCheckedRows()) {
263: if (r.isChecked())
264: sb.append(r.get(columnIndex)).append(delimiter);
265: }
266:
267: s = sb.length() > 0 ? sb.substring(0, sb.length() - 1)
268: : "";
269: } else {
270: s = gb.getSelectedRow().get(columnIndex).toString();
271: }
272: return s;
273: }
274:
275: public void setValue(Object value) {
276: GridBox gb = getGridBox();
277: if (gb == null)
278: throw new IllegalStateException("gridBox == null");
279: if (gb.getColumns().size() == 0)
280: return;
281:
282: String delimiter;
283: int columnIndex;
284: delimiter = this .delimiter;
285: columnIndex = this .columnIndex;
286:
287: if (columnIndex >= gb.getColumns().size())
288: throw new IllegalStateException(
289: "columnIndex >= gridBox.getColumns().size()");
290: String s;
291:
292: if (value == null) {
293: s = "";
294: } else if (value instanceof String) {
295: s = (String) value;
296: } else {
297: s = value.toString();
298: }
299:
300: if (gb.isVisibleCheckBoxes()) {
301: StringBuilder sb = new StringBuilder();
302: sb.append(delimiter).append(s).append(delimiter);
303: s = sb.toString();
304: sb.setLength(0);
305:
306: for (List l : gb.getRows()) {
307: GridBox.Row r = (GridBox.Row) l;
308: sb.append(delimiter).append(r.get(columnIndex))
309: .append(delimiter);
310: r.setChecked(s.indexOf(sb.toString()) != -1);
311: sb.setLength(0);
312: }
313: } else {
314: List<GridBox.Row> rows = gb.getRows();
315:
316: for (List l : rows) {
317: GridBox.Row r = (GridBox.Row) l;
318:
319: if (r.getChild() == null) {
320: value = r.get(columnIndex);
321:
322: if (value != null && value.toString().equals(s)) {
323: r.setSelected(true);
324: break;
325: }
326: }
327: }
328: }
329: }
330:
331: public int getColumnIndex() {
332: return columnIndex;
333: }
334:
335: public void setColumnIndex(int columnIndex) {
336: if (columnIndex < 0 || columnIndex > 128)
337: throw new IllegalArgumentException(
338: "columnIndex < 0 || columnIndex > 128");
339: this .columnIndex = columnIndex;
340: }
341:
342: public String getDelimiter() {
343: return delimiter;
344: }
345:
346: public void setDelimiter(String delimiter) {
347: if (delimiter == null || delimiter.length() == 0)
348: throw new IllegalArgumentException(
349: "delimiter == null || delimiter.length() == 0");
350: this .delimiter = delimiter;
351: }
352:
353: //TODO: This is not correct when you have fixed-width columns (i.e. non-auto-size columns)
354: public int getOptimalWidth() {
355: int width = 0;
356: List<GridBox.Column> cols = ddc.getColumns();
357: boolean[] visibleState = new boolean[cols.size()];
358:
359: for (int i = 0, size = cols.size(); i < size; i++) {
360: visibleState[i] = cols.get(i).isVisible();
361: }
362:
363: if (ddc.isVisibleHeader()) {
364: int cnt = 0;
365:
366: for (int i = 0, size = cols.size(); i < size; i++) {
367: if (visibleState[i]) {
368: GridBox.Column col = cols.get(i);
369: String name = col.getHeader().getText();
370: if (name.length() == 0)
371: name = col.getName();
372: int len = name.length();
373: String upperName = name.toUpperCase();
374: if (name.equals(upperName))
375: len = len / 7 + 1;
376: if (len < 4)
377: len++;
378: cnt += len;
379: }
380: }
381:
382: if (cnt > width)
383: width = cnt;
384: }
385:
386: for (Row r : ddc.getRows()) {
387: int cnt = 0;
388:
389: for (int i = 0, size = r.size(); i < size; i++) {
390: if (visibleState[i]) {
391: Object cell = r.get(i);
392:
393: if (cell != null) {
394: String value = cell.toString();
395: int len = value.length();
396: String upperValue = value.toUpperCase();
397: if (value.equals(upperValue))
398: len += len / 7 + 1;
399: if (len < 4)
400: len++;
401: cnt += len;
402: }
403: }
404: }
405:
406: if (cnt > width)
407: width = cnt;
408: }
409:
410: if (ddc.isVisibleCheckBoxes())
411: width += 3;
412:
413: width *= 6.6; //TODO: Hardcoded character width.
414:
415: if (ddc.getParent() instanceof DropDownGridBox) {
416: int ddWidth = ((DropDownGridBox) ddc.getParent())
417: .getWidth();
418: if (ddWidth > width)
419: width = ddWidth;
420: }
421:
422: Application app = Application.current();
423: int maxWidth = app != null ? app.getFrame().getInnerWidth() / 2 - 10
424: : DEFAULT_MAX_WIDTH;
425: if (width > maxWidth)
426: width = maxWidth;
427: if (width < MIN_SIZE)
428: width = MIN_SIZE;
429: return width;
430: }
431:
432: public int getOptimalHeight() {
433: int height = ddc.getRows().size();
434: if (height < 3)
435: height = 3;
436: height *= 14; //TODO: Hardcoded row height
437: height += 10; //TODO: Hardcoded fudge factor for border
438: if (ddc.isVisibleHeader())
439: height += 16; //TODO: Hardcoded column header size
440:
441: Application app = Application.current();
442: int maxHeight = app != null ? app.getFrame()
443: .getInnerHeight() / 2 - 20 : DEFAULT_MAX_HEIGHT;
444: if (ddc.getParent() instanceof DropDownGridBox)
445: maxHeight -= ((DropDownGridBox) ddc.getParent())
446: .getHeight();
447: if (height > maxHeight)
448: height = maxHeight;
449: if (height < MIN_SIZE)
450: height = MIN_SIZE;
451: return height;
452: }
453: }
454:
455: /**
456: * Constructs a new DropDownGridBox with no text.
457: */
458: public DropDownGridBox() {
459: this (null);
460: }
461:
462: /**
463: * Constructs a new DropDownGridBox with the specified text.
464: */
465: public DropDownGridBox(String text) {
466: super (new DefaultView(), new GridBox());
467: ((DefaultView) getView()).init(this, this.getComponent());
468: if (text != null)
469: setText(text);
470: }
471: }
|