001: /*
002: $Id: SwingBuilder.java 4247 2006-11-19 19:00:19Z mcspanky $
003:
004: Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005:
006: Redistribution and use of this software and associated documentation
007: ("Software"), with or without modification, are permitted provided
008: that the following conditions are met:
009:
010: 1. Redistributions of source code must retain copyright
011: statements and notices. Redistributions must also contain a
012: copy of this document.
013:
014: 2. Redistributions in binary form must reproduce the
015: above copyright notice, this list of conditions and the
016: following disclaimer in the documentation and/or other
017: materials provided with the distribution.
018:
019: 3. The name "groovy" must not be used to endorse or promote
020: products derived from this Software without prior written
021: permission of The Codehaus. For written permission,
022: please contact info@codehaus.org.
023:
024: 4. Products derived from this Software may not be called "groovy"
025: nor may "groovy" appear in their names without prior written
026: permission of The Codehaus. "groovy" is a registered
027: trademark of The Codehaus.
028:
029: 5. Due credit should be given to The Codehaus -
030: http://groovy.codehaus.org/
031:
032: THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033: ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034: NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
036: THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042: ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043: OF THE POSSIBILITY OF SUCH DAMAGE.
044:
045: */
046: package groovy.swing;
047:
048: import groovy.lang.Closure;
049: import groovy.lang.MissingMethodException;
050:
051: import groovy.model.DefaultTableModel;
052: import groovy.model.ValueHolder;
053: import groovy.model.ValueModel;
054:
055: import groovy.swing.impl.ComponentFacade;
056: import groovy.swing.impl.ContainerFacade;
057: import groovy.swing.impl.DefaultAction;
058: import groovy.swing.impl.Factory;
059: import groovy.swing.impl.Startable;
060: import groovy.swing.impl.TableLayout;
061: import groovy.swing.impl.TableLayoutCell;
062: import groovy.swing.impl.TableLayoutRow;
063:
064: import groovy.util.BuilderSupport;
065:
066: import java.awt.BorderLayout;
067: import java.awt.CardLayout;
068: import java.awt.Component;
069: import java.awt.Container;
070: import java.awt.Dimension;
071: import java.awt.Dialog;
072: import java.awt.FlowLayout;
073: import java.awt.Frame;
074: import java.awt.GridBagConstraints;
075: import java.awt.GridBagLayout;
076: import java.awt.GridLayout;
077: import java.awt.LayoutManager;
078: import java.awt.Window;
079:
080: import java.text.Format;
081:
082: import java.util.ArrayList;
083: import java.util.Collections;
084: import java.util.HashMap;
085: import java.util.Iterator;
086: import java.util.LinkedList;
087: import java.util.List;
088: import java.util.Map;
089: import java.util.Vector;
090: import java.util.logging.Level;
091: import java.util.logging.Logger;
092:
093: import javax.swing.AbstractButton;
094: import javax.swing.Action;
095: import javax.swing.Box;
096: import javax.swing.BoxLayout;
097: import javax.swing.ButtonGroup;
098: import javax.swing.DefaultBoundedRangeModel;
099: import javax.swing.JButton;
100: import javax.swing.JCheckBox;
101: import javax.swing.JCheckBoxMenuItem;
102: import javax.swing.JColorChooser;
103: import javax.swing.JComboBox;
104: import javax.swing.JComponent;
105: import javax.swing.JDesktopPane;
106: import javax.swing.JDialog;
107: import javax.swing.JEditorPane;
108: import javax.swing.JFileChooser;
109: import javax.swing.JFormattedTextField;
110: import javax.swing.JFrame;
111: import javax.swing.JInternalFrame;
112: import javax.swing.JLabel;
113: import javax.swing.JLayeredPane;
114: import javax.swing.JList;
115: import javax.swing.JMenu;
116: import javax.swing.JMenuBar;
117: import javax.swing.JMenuItem;
118: import javax.swing.JOptionPane;
119: import javax.swing.JPanel;
120: import javax.swing.JPasswordField;
121: import javax.swing.JPopupMenu;
122: import javax.swing.JProgressBar;
123: import javax.swing.JRadioButton;
124: import javax.swing.JRadioButtonMenuItem;
125: import javax.swing.JScrollBar;
126: import javax.swing.JScrollPane;
127: import javax.swing.JSeparator;
128: import javax.swing.JSlider;
129: import javax.swing.JSpinner;
130: import javax.swing.JSplitPane;
131: import javax.swing.JTabbedPane;
132: import javax.swing.JTable;
133: import javax.swing.JTextArea;
134: import javax.swing.JTextField;
135: import javax.swing.JTextPane;
136: import javax.swing.JToggleButton;
137: import javax.swing.JToolBar;
138: import javax.swing.JToolTip;
139: import javax.swing.JTree;
140: import javax.swing.JViewport;
141: import javax.swing.JWindow;
142: import javax.swing.KeyStroke;
143: import javax.swing.OverlayLayout;
144: import javax.swing.RootPaneContainer;
145: import javax.swing.SpinnerDateModel;
146: import javax.swing.SpinnerListModel;
147: import javax.swing.SpinnerNumberModel;
148: import javax.swing.SpringLayout;
149: import javax.swing.table.TableColumn;
150: import javax.swing.table.TableModel;
151:
152: import org.codehaus.groovy.runtime.InvokerHelper;
153:
154: /**
155: * A helper class for creating Swing widgets using GroovyMarkup
156: *
157: * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
158: * @version $Revision: 4247 $
159: */
160: public class SwingBuilder extends BuilderSupport {
161:
162: private Logger log = Logger.getLogger(getClass().getName());
163: private Map factories = new HashMap();
164: private Object constraints;
165: private Map passThroughNodes = new HashMap();
166: private Map widgets = new HashMap();
167: // tracks all containing windows, for auto-owned dialogs
168: private LinkedList containingWindows = new LinkedList();
169:
170: public SwingBuilder() {
171: registerWidgets();
172: }
173:
174: public Object getProperty(String name) {
175: Object widget = widgets.get(name);
176: if (widget == null) {
177: return super .getProperty(name);
178: }
179: return widget;
180: }
181:
182: protected void setParent(Object parent, Object child) {
183: if (child instanceof Action) {
184: Action action = (Action) child;
185: try {
186: InvokerHelper.setProperty(parent, "action", action);
187: } catch (RuntimeException re) {
188: // must not have an action property...
189: // so we ignore it and go on
190: }
191: Object keyStroke = action.getValue("KeyStroke");
192: //System.out.println("keystroke: " + keyStroke + " for: " + action);
193: if (parent instanceof JComponent) {
194: JComponent component = (JComponent) parent;
195: KeyStroke stroke = null;
196: if (keyStroke instanceof String) {
197: stroke = KeyStroke.getKeyStroke((String) keyStroke);
198: } else if (keyStroke instanceof KeyStroke) {
199: stroke = (KeyStroke) keyStroke;
200: }
201: if (stroke != null) {
202: String key = action.toString();
203: component.getInputMap().put(stroke, key);
204: component.getActionMap().put(key, action);
205: }
206: }
207: } else if (child instanceof LayoutManager) {
208: if (parent instanceof RootPaneContainer) {
209: RootPaneContainer rpc = (RootPaneContainer) parent;
210: parent = rpc.getContentPane();
211: }
212: InvokerHelper.setProperty(parent, "layout", child);
213: } else if (child instanceof JToolTip
214: && parent instanceof JComponent) {
215: ((JToolTip) child).setComponent((JComponent) parent);
216: } else if (parent instanceof JTable
217: && child instanceof TableColumn) {
218: JTable table = (JTable) parent;
219: TableColumn column = (TableColumn) child;
220: table.addColumn(column);
221: } else if (parent instanceof JTabbedPane
222: && child instanceof Component) {
223: JTabbedPane tabbedPane = (JTabbedPane) parent;
224: tabbedPane.add((Component) child);
225: } else if (child instanceof Window) {
226: // do nothing. owner of window is set elsewhere, and this
227: // shouldn't get added to any parent as a child
228: // if it is a top level component anyway
229: } else {
230: Component component = null;
231: if (child instanceof Component) {
232: component = (Component) child;
233: } else if (child instanceof ComponentFacade) {
234: ComponentFacade facade = (ComponentFacade) child;
235: component = facade.getComponent();
236: }
237: if (component != null) {
238: if (parent instanceof JFrame
239: && component instanceof JMenuBar) {
240: JFrame frame = (JFrame) parent;
241: frame.setJMenuBar((JMenuBar) component);
242: } else if (parent instanceof RootPaneContainer) {
243: RootPaneContainer rpc = (RootPaneContainer) parent;
244: if (constraints != null) {
245: rpc.getContentPane()
246: .add(component, constraints);
247: } else {
248: rpc.getContentPane().add(component);
249: }
250: } else if (parent instanceof JScrollPane) {
251: JScrollPane scrollPane = (JScrollPane) parent;
252: if (child instanceof JViewport) {
253: scrollPane.setViewport((JViewport) component);
254: } else {
255: scrollPane.setViewportView(component);
256: }
257: } else if (parent instanceof JSplitPane) {
258: JSplitPane splitPane = (JSplitPane) parent;
259: if (splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) {
260: if (splitPane.getTopComponent() == null) {
261: splitPane.setTopComponent(component);
262: } else {
263: splitPane.setBottomComponent(component);
264: }
265: } else {
266: if (splitPane.getLeftComponent() == null) {
267: splitPane.setLeftComponent(component);
268: } else {
269: splitPane.setRightComponent(component);
270: }
271: }
272: } else if (parent instanceof JMenuBar
273: && component instanceof JMenu) {
274: JMenuBar menuBar = (JMenuBar) parent;
275: menuBar.add((JMenu) component);
276: } else if (parent instanceof Container) {
277: Container container = (Container) parent;
278: if (constraints != null) {
279: container.add(component, constraints);
280: } else {
281: container.add(component);
282: }
283: } else if (parent instanceof ContainerFacade) {
284: ContainerFacade facade = (ContainerFacade) parent;
285: facade.addComponent(component);
286: }
287: }
288: }
289: }
290:
291: protected void nodeCompleted(Object parent, Object node) {
292: // set models after the node has been completed
293: if (node instanceof TableModel && parent instanceof JTable) {
294: JTable table = (JTable) parent;
295: TableModel model = (TableModel) node;
296: table.setModel(model);
297: }
298: if (node instanceof Startable) {
299: Startable startable = (Startable) node;
300: startable.start();
301: }
302: if (node instanceof Window) {
303: if (!containingWindows.isEmpty()
304: && containingWindows.getLast() == node) {
305: containingWindows.removeLast();
306: }
307: }
308: }
309:
310: protected Object createNode(Object name) {
311: return createNode(name, Collections.EMPTY_MAP);
312: }
313:
314: protected Object createNode(Object name, Object value) {
315: if (passThroughNodes.containsKey(name)
316: && (value != null)
317: && ((Class) passThroughNodes.get(name))
318: .isAssignableFrom(value.getClass())) {
319: // value may need to go into containing windows list
320: if (value instanceof Window) {
321: containingWindows.add(value);
322: }
323: return value;
324: } else if (value instanceof String) {
325: Object widget = createNode(name);
326: if (widget != null) {
327: InvokerHelper.invokeMethod(widget, "setText", value);
328: }
329: return widget;
330: } else {
331: throw new MissingMethodException((String) name, getClass(),
332: new Object[] { value }, false);
333: }
334: }
335:
336: protected Object createNode(Object name, Map attributes,
337: Object value) {
338: if (passThroughNodes.containsKey(name)
339: && (value != null)
340: && ((Class) passThroughNodes.get(name))
341: .isAssignableFrom(value.getClass())) {
342: // value may need to go into containing windows list
343: if (value instanceof Window) {
344: containingWindows.add(value);
345: }
346: handleWidgetAttributes(value, attributes);
347: return value;
348: } else {
349: Object widget = createNode(name, attributes);
350: if (widget != null) {
351: InvokerHelper.invokeMethod(widget, "setText", value
352: .toString());
353: }
354: return widget;
355: }
356: }
357:
358: protected Object createNode(Object name, Map attributes) {
359: String widgetName = (String) attributes.remove("id");
360: constraints = attributes.remove("constraints");
361: Object widget = null;
362: if (passThroughNodes.containsKey(name)) {
363: widget = attributes.get(name);
364: if ((widget != null)
365: && ((Class) passThroughNodes.get(name))
366: .isAssignableFrom(widget.getClass())) {
367: // value may need to go into containing windows list
368: if (widget instanceof Window) {
369: containingWindows.add(widget);
370: }
371: attributes.remove(name);
372: } else {
373: widget = null;
374: }
375: }
376: if (widget == null) {
377: Factory factory = (Factory) factories.get(name);
378: if (factory != null) {
379: try {
380: widget = factory.newInstance(attributes);
381: if (widgetName != null) {
382: widgets.put(widgetName, widget);
383: }
384: if (widget == null) {
385: log.log(Level.WARNING, "Factory for name: "
386: + name + " returned null");
387: } else {
388: if (log.isLoggable(Level.FINE)) {
389: log.fine("For name: " + name
390: + " created widget: " + widget);
391: }
392: }
393: } catch (Exception e) {
394: throw new RuntimeException(
395: "Failed to create component for" + name
396: + " reason: " + e, e);
397: }
398: } else {
399: log.log(Level.WARNING,
400: "Could not find match for name: " + name);
401: }
402: }
403: handleWidgetAttributes(widget, attributes);
404: return widget;
405: }
406:
407: protected void handleWidgetAttributes(Object widget, Map attributes) {
408: if (widget != null) {
409: if (widget instanceof Action) {
410: /** @todo we could move this custom logic into the MetaClass for Action */
411: Action action = (Action) widget;
412:
413: Closure closure = (Closure) attributes
414: .remove("closure");
415: if (closure != null && action instanceof DefaultAction) {
416: DefaultAction defaultAction = (DefaultAction) action;
417: defaultAction.setClosure(closure);
418: }
419:
420: Object accel = attributes.remove("accelerator");
421: KeyStroke stroke = null;
422: if (accel instanceof KeyStroke) {
423: stroke = (KeyStroke) accel;
424: } else if (accel != null) {
425: stroke = KeyStroke.getKeyStroke(accel.toString());
426: }
427: action.putValue(Action.ACCELERATOR_KEY, stroke);
428:
429: Object mnemonic = attributes.remove("mnemonic");
430: if ((mnemonic != null) && !(mnemonic instanceof Number)) {
431: mnemonic = new Integer(mnemonic.toString()
432: .charAt(0));
433: }
434: action.putValue(Action.MNEMONIC_KEY, mnemonic);
435:
436: for (Iterator iter = attributes.entrySet().iterator(); iter
437: .hasNext();) {
438: Map.Entry entry = (Map.Entry) iter.next();
439: String actionName = (String) entry.getKey(); // todo dk: misleading naming. this can be any property name
440:
441: // typically standard Action names start with upper case, so lets upper case it
442: actionName = capitalize(actionName); // todo dk: in general, this shouldn't be capitalized
443: Object value = entry.getValue();
444:
445: action.putValue(actionName, value);
446: }
447:
448: } else {
449: // some special cases...
450: if (attributes.containsKey("buttonGroup")) {
451: Object o = attributes.get("buttonGroup");
452: if ((o instanceof ButtonGroup)
453: && (widget instanceof AbstractButton)) {
454: ((AbstractButton) widget).getModel().setGroup(
455: (ButtonGroup) o);
456: attributes.remove("buttonGroup");
457: }
458: }
459:
460: // this next statement nd if/else is a workaround until GROOVY-305 is fixed
461: Object mnemonic = attributes.remove("mnemonic");
462: if ((mnemonic != null) && (mnemonic instanceof Number)) {
463: InvokerHelper.setProperty(widget, "mnemonic",
464: new Character((char) ((Number) mnemonic)
465: .intValue()));
466: } else if (mnemonic != null) {
467: InvokerHelper
468: .setProperty(widget, "mnemonic",
469: new Character(mnemonic.toString()
470: .charAt(0)));
471: }
472:
473: // set the properties
474: for (Iterator iter = attributes.entrySet().iterator(); iter
475: .hasNext();) {
476: Map.Entry entry = (Map.Entry) iter.next();
477: String property = entry.getKey().toString();
478: Object value = entry.getValue();
479: InvokerHelper.setProperty(widget, property, value);
480: }
481: }
482: }
483: }
484:
485: protected String capitalize(String text) {
486: char ch = text.charAt(0);
487: if (Character.isUpperCase(ch)) {
488: return text;
489: }
490: StringBuffer buffer = new StringBuffer(text.length());
491: buffer.append(Character.toUpperCase(ch));
492: buffer.append(text.substring(1));
493: return buffer.toString();
494: }
495:
496: protected void registerWidgets() {
497: //
498: // non-widget support classes
499: //
500: registerBeanFactory("action", DefaultAction.class);
501: passThroughNodes.put("action", javax.swing.Action.class);
502: registerBeanFactory("buttonGroup", ButtonGroup.class);
503: registerFactory("map", new Factory() { // todo dk: is that still needed?
504: public Object newInstance(Map properties)
505: throws InstantiationException,
506: InstantiationException,
507: IllegalAccessException {
508: return properties;
509: }
510: });
511: // ulimate pass through type
512: passThroughNodes.put("widget", java.awt.Component.class);
513:
514: //
515: // standalone window classes
516: //
517: registerFactory("dialog", new Factory() {
518: public Object newInstance(Map properties)
519: throws InstantiationException,
520: InstantiationException, IllegalAccessException {
521: return createDialog(properties);
522: }
523: });
524: registerFactory("frame", new Factory() {
525: public Object newInstance(Map properties)
526: throws InstantiationException,
527: InstantiationException, IllegalAccessException {
528: return createFrame(properties);
529: }
530: });
531: registerBeanFactory("fileChooser", JFileChooser.class);
532: registerFactory("frame", new Factory() { // todo dk: frame registered twice ???
533: public Object newInstance(Map properties)
534: throws InstantiationException,
535: InstantiationException,
536: IllegalAccessException {
537: return createFrame(properties);
538: }
539: });
540: registerBeanFactory("optionPane", JOptionPane.class);
541: registerFactory("window", new Factory() {
542: public Object newInstance(Map properties)
543: throws InstantiationException,
544: InstantiationException, IllegalAccessException {
545: return createWindow(properties);
546: }
547: });
548:
549: //
550: // widgets
551: //
552: registerBeanFactory("button", JButton.class);
553: registerBeanFactory("checkBox", JCheckBox.class);
554: registerBeanFactory("checkBoxMenuItem", JCheckBoxMenuItem.class);
555: registerBeanFactory("colorChooser", JColorChooser.class);
556: registerFactory("comboBox", new Factory() {
557: public Object newInstance(Map properties)
558: throws InstantiationException,
559: InstantiationException, IllegalAccessException {
560: return createComboBox(properties);
561: }
562: });
563: registerBeanFactory("desktopPane", JDesktopPane.class);
564: registerBeanFactory("editorPane", JEditorPane.class);
565: registerFactory("formattedTextField", new Factory() {
566: public Object newInstance(Map properties)
567: throws InstantiationException,
568: InstantiationException, IllegalAccessException {
569: return createFormattedTextField(properties);
570: }
571: });
572: registerBeanFactory("internalFrame", JInternalFrame.class);
573: registerBeanFactory("label", JLabel.class);
574: registerBeanFactory("layeredPane", JLayeredPane.class);
575: registerBeanFactory("list", JList.class);
576: registerBeanFactory("menu", JMenu.class);
577: registerBeanFactory("menuBar", JMenuBar.class);
578: registerBeanFactory("menuItem", JMenuItem.class);
579: registerBeanFactory("panel", JPanel.class);
580: registerBeanFactory("passwordField", JPasswordField.class);
581: registerBeanFactory("popupMenu", JPopupMenu.class);
582: registerBeanFactory("progressBar", JProgressBar.class);
583: registerBeanFactory("radioButton", JRadioButton.class);
584: registerBeanFactory("radioButtonMenuItem",
585: JRadioButtonMenuItem.class);
586: registerBeanFactory("scrollBar", JScrollBar.class);
587: registerBeanFactory("scrollPane", JScrollPane.class);
588: registerBeanFactory("separator", JSeparator.class);
589: registerBeanFactory("slider", JSlider.class);
590: registerBeanFactory("spinner", JSpinner.class);
591: registerFactory("splitPane", new Factory() {
592: public Object newInstance(Map properties) {
593: JSplitPane answer = new JSplitPane();
594: answer.setLeftComponent(null);
595: answer.setRightComponent(null);
596: answer.setTopComponent(null);
597: answer.setBottomComponent(null);
598: return answer;
599: }
600: });
601: registerBeanFactory("tabbedPane", JTabbedPane.class);
602: registerBeanFactory("table", JTable.class);
603: registerBeanFactory("textArea", JTextArea.class);
604: registerBeanFactory("textPane", JTextPane.class);
605: registerBeanFactory("textField", JTextField.class);
606: registerBeanFactory("toggleButton", JToggleButton.class);
607: registerBeanFactory("toolBar", JToolBar.class);
608: //registerBeanFactory("tooltip", JToolTip.class); // doens't work, user toolTipText property
609: registerBeanFactory("tree", JTree.class);
610: registerBeanFactory("viewport", JViewport.class); // sub class?
611:
612: //
613: // MVC models
614: //
615: registerBeanFactory("boundedRangeModel",
616: DefaultBoundedRangeModel.class);
617:
618: // spinner models
619: registerBeanFactory("spinnerDateModel", SpinnerDateModel.class);
620: registerBeanFactory("spinnerListModel", SpinnerListModel.class);
621: registerBeanFactory("spinnerNumberModel",
622: SpinnerNumberModel.class);
623:
624: // table models
625: registerFactory("tableModel", new Factory() {
626: public Object newInstance(Map properties) {
627: ValueModel model = (ValueModel) properties
628: .remove("model");
629: if (model == null) {
630: Object list = properties.remove("list");
631: if (list == null) {
632: list = new ArrayList();
633: }
634: model = new ValueHolder(list);
635: }
636: return new DefaultTableModel(model);
637: }
638: });
639: passThroughNodes.put("tableModel",
640: javax.swing.table.TableModel.class);
641:
642: registerFactory("propertyColumn", new Factory() {
643: public Object newInstance(Map properties) {
644: Object current = getCurrent();
645: if (current instanceof DefaultTableModel) {
646: DefaultTableModel model = (DefaultTableModel) current;
647: Object header = properties.remove("header");
648: if (header == null) {
649: header = "";
650: }
651: String property = (String) properties
652: .remove("propertyName");
653: if (property == null) {
654: throw new IllegalArgumentException(
655: "Must specify a property for a propertyColumn");
656: }
657: Class type = (Class) properties.remove("type");
658: if (type == null) {
659: type = Object.class;
660: }
661: return model.addPropertyColumn(header, property,
662: type);
663: } else {
664: throw new RuntimeException(
665: "propertyColumn must be a child of a tableModel");
666: }
667: }
668: });
669:
670: registerFactory("closureColumn", new Factory() {
671: public Object newInstance(Map properties) {
672: Object current = getCurrent();
673: if (current instanceof DefaultTableModel) {
674: DefaultTableModel model = (DefaultTableModel) current;
675: Object header = properties.remove("header");
676: if (header == null) {
677: header = "";
678: }
679: Closure readClosure = (Closure) properties
680: .remove("read");
681: if (readClosure == null) {
682: throw new IllegalArgumentException(
683: "Must specify 'read' Closure property for a closureColumn");
684: }
685: Closure writeClosure = (Closure) properties
686: .remove("write");
687: Class type = (Class) properties.remove("type");
688: if (type == null) {
689: type = Object.class;
690: }
691: return model.addClosureColumn(header, readClosure,
692: writeClosure, type);
693: } else {
694: throw new RuntimeException(
695: "propertyColumn must be a child of a tableModel");
696: }
697: }
698: });
699:
700: //Standard Layouts
701: registerBeanFactory("borderLayout", BorderLayout.class);
702: registerBeanFactory("cardLayout", CardLayout.class);
703: registerBeanFactory("flowLayout", FlowLayout.class);
704: registerBeanFactory("gridBagLayout", GridBagLayout.class);
705: registerBeanFactory("gridLayout", GridLayout.class);
706: registerBeanFactory("overlayLayout", OverlayLayout.class);
707: registerBeanFactory("springLayout", SpringLayout.class);
708: registerBeanFactory("gridBagConstraints",
709: GridBagConstraints.class);
710: registerBeanFactory("gbc", GridBagConstraints.class); // shortcut name
711:
712: // box layout
713: registerFactory("boxLayout", new Factory() {
714: public Object newInstance(Map properties)
715: throws InstantiationException,
716: InstantiationException, IllegalAccessException {
717: return createBoxLayout(properties);
718: }
719: });
720:
721: // Box related layout components
722: registerFactory("hbox", new Factory() {
723: public Object newInstance(Map properties) {
724: return Box.createHorizontalBox();
725: }
726: });
727: registerFactory("hglue", new Factory() {
728: public Object newInstance(Map properties) {
729: return Box.createHorizontalGlue();
730: }
731: });
732: registerFactory("hstrut", new Factory() {
733: public Object newInstance(Map properties) {
734: try {
735: Object num = properties.remove("width");
736: if (num instanceof Number) {
737: return Box.createHorizontalStrut(((Number) num)
738: .intValue());
739: } else {
740: return Box.createHorizontalStrut(6);
741: }
742: } catch (RuntimeException re) {
743: re.printStackTrace(System.out);
744: throw re;
745: }
746: }
747: });
748: registerFactory("vbox", new Factory() {
749: public Object newInstance(Map properties) {
750: return Box.createVerticalBox();
751: }
752: });
753: registerFactory("vglue", new Factory() {
754: public Object newInstance(Map properties) {
755: return Box.createVerticalGlue();
756: }
757: });
758: registerFactory("vstrut", new Factory() {
759: public Object newInstance(Map properties) {
760: Object num = properties.remove("height");
761: if (num instanceof Number) {
762: return Box.createVerticalStrut(((Number) num)
763: .intValue());
764: } else {
765: return Box.createVerticalStrut(6);
766: }
767: }
768: });
769: registerFactory("glue", new Factory() {
770: public Object newInstance(Map properties) {
771: return Box.createGlue();
772: }
773: });
774: registerFactory("rigidArea", new Factory() {
775: public Object newInstance(Map properties) {
776: Dimension dim;
777: Object o = properties.remove("size");
778: if (o instanceof Dimension) {
779: dim = (Dimension) o;
780: } else {
781: int w, h;
782: o = properties.remove("width");
783: w = ((o instanceof Number)) ? ((Number) o)
784: .intValue() : 6;
785: o = properties.remove("height");
786: h = ((o instanceof Number)) ? ((Number) o)
787: .intValue() : 6;
788: dim = new Dimension(w, h);
789: }
790: return Box.createRigidArea(dim);
791: }
792: });
793:
794: // table layout
795: registerBeanFactory("tableLayout", TableLayout.class);
796: registerFactory("tr", new Factory() {
797: public Object newInstance(Map properties) {
798: Object parent = getCurrent();
799: if (parent instanceof TableLayout) {
800: return new TableLayoutRow((TableLayout) parent);
801: } else {
802: throw new RuntimeException(
803: "'tr' must be within a 'tableLayout'");
804: }
805: }
806: });
807: registerFactory("td", new Factory() {
808: public Object newInstance(Map properties) {
809: Object parent = getCurrent();
810: if (parent instanceof TableLayoutRow) {
811: return new TableLayoutCell((TableLayoutRow) parent);
812: } else {
813: throw new RuntimeException(
814: "'td' must be within a 'tr'");
815: }
816: }
817: });
818: }
819:
820: protected Object createBoxLayout(Map properties) {
821: Object parent = getCurrent();
822: if (parent instanceof Container) {
823: Object axisObject = properties.remove("axis");
824: int axis = BoxLayout.X_AXIS;
825: if (axisObject != null) {
826: Integer i = (Integer) axisObject;
827: axis = i.intValue();
828: }
829:
830: Container target = (Container) parent;
831: if (target instanceof RootPaneContainer) {
832: target = ((RootPaneContainer) target).getContentPane();
833: }
834: BoxLayout answer = new BoxLayout(target, axis);
835:
836: // now lets try set the layout property
837: InvokerHelper.setProperty(parent, "layout", answer);
838: return answer;
839: } else {
840: throw new RuntimeException(
841: "Must be nested inside a Container");
842: }
843: }
844:
845: protected Object createDialog(Map properties) {
846: JDialog dialog;
847: Object owner = properties.remove("owner");
848: // if owner not explicit, use the last window type in the list
849: if ((owner == null) && !containingWindows.isEmpty()) {
850: owner = containingWindows.getLast();
851: }
852: if (owner instanceof Frame) {
853: dialog = new JDialog((Frame) owner);
854: } else if (owner instanceof Dialog) {
855: dialog = new JDialog((Dialog) owner);
856: } else {
857: dialog = new JDialog();
858: }
859: containingWindows.add(dialog);
860: return dialog;
861: }
862:
863: /**
864: * Uses 'format," or "value," (in order)
865: *
866: */
867: protected Object createFormattedTextField(Map properties) {
868: JFormattedTextField ftf;
869: if (properties.containsKey("format")) {
870: ftf = new JFormattedTextField((Format) properties
871: .remove("format"));
872: } else if (properties.containsKey("value")) {
873: ftf = new JFormattedTextField(properties.remove("value"));
874: } else {
875: ftf = new JFormattedTextField();
876: }
877: return ftf;
878: }
879:
880: protected Object createFrame(Map properties) {
881: JFrame frame = new JFrame();
882: containingWindows.add(frame);
883: return frame;
884: }
885:
886: protected Object createWindow(Map properties) {
887: JWindow window;
888: Object owner = properties.remove("owner");
889: // if owner not explicit, use the last window type in the list
890: if ((owner == null) && !containingWindows.isEmpty()) {
891: owner = containingWindows.getLast();
892: }
893: if (owner instanceof Frame) {
894: window = new JWindow((Frame) owner);
895: } else if (owner instanceof Window) {
896: window = new JWindow((Window) owner);
897: } else {
898: window = new JWindow();
899: }
900: containingWindows.add(window);
901: return window;
902: }
903:
904: protected Object createComboBox(Map properties) {
905: Object items = properties.remove("items");
906: if (items instanceof Vector) {
907: return new JComboBox((Vector) items);
908: } else if (items instanceof List) {
909: List list = (List) items;
910: return new JComboBox(list.toArray());
911: } else if (items instanceof Object[]) {
912: return new JComboBox((Object[]) items);
913: } else {
914: return new JComboBox();
915: }
916: }
917:
918: protected void registerBeanFactory(String name,
919: final Class beanClass) {
920: registerFactory(name, new Factory() {
921: public Object newInstance(Map properties)
922: throws InstantiationException,
923: IllegalAccessException {
924: return beanClass.newInstance();
925: }
926: });
927:
928: }
929:
930: protected void registerFactory(String name, Factory factory) {
931: factories.put(name, factory);
932: }
933: }
|