001 /*
002 $Id: SwingBuilder.java,v 1.13 2005/06/10 09:55:30 cstein Exp $
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: 1.13 $
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 }
199 else if (keyStroke instanceof KeyStroke) {
200 stroke = (KeyStroke) keyStroke;
201 }
202 if (stroke != null) {
203 String key = action.toString();
204 component.getInputMap().put(stroke, key);
205 component.getActionMap().put(key, action);
206 }
207 }
208 }
209 else if (child instanceof LayoutManager) {
210 if (parent instanceof RootPaneContainer) {
211 RootPaneContainer rpc = (RootPaneContainer) parent;
212 parent = rpc.getContentPane();
213 }
214 InvokerHelper.setProperty(parent, "layout", child);
215 }
216 else if (child instanceof JToolTip && parent instanceof JComponent) {
217 ((JToolTip)child).setComponent((JComponent)parent);
218 }
219 else if (parent instanceof JTable && child instanceof TableColumn) {
220 JTable table = (JTable) parent;
221 TableColumn column = (TableColumn) child;
222 table.addColumn(column);
223 }
224 else if (parent instanceof JTabbedPane && child instanceof Component) {
225 JTabbedPane tabbedPane = (JTabbedPane) parent;
226 tabbedPane.add((Component)child);
227 }
228 else if (child instanceof Window) {
229 // do nothing. owner of window is set elsewhere, and this
230 // shouldn't get added to any parent as a child
231 // if it is a top level component anyway
232 }
233 else {
234 Component component = null;
235 if (child instanceof Component) {
236 component = (Component) child;
237 }
238 else if (child instanceof ComponentFacade) {
239 ComponentFacade facade = (ComponentFacade) child;
240 component = facade.getComponent();
241 }
242 if (component != null) {
243 if (parent instanceof JFrame && component instanceof JMenuBar) {
244 JFrame frame = (JFrame) parent;
245 frame.setJMenuBar((JMenuBar) component);
246 }
247 else if (parent instanceof RootPaneContainer) {
248 RootPaneContainer rpc = (RootPaneContainer) parent;
249 rpc.getContentPane().add(component);
250 }
251 else if (parent instanceof JScrollPane) {
252 JScrollPane scrollPane = (JScrollPane) parent;
253 if (child instanceof JViewport) {
254 scrollPane.setViewport((JViewport)component);
255 }
256 else {
257 scrollPane.setViewportView(component);
258 }
259 }
260 else if (parent instanceof JSplitPane) {
261 JSplitPane splitPane = (JSplitPane) parent;
262 if (splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) {
263 if (splitPane.getTopComponent() == null) {
264 splitPane.setTopComponent(component);
265 }
266 else {
267 splitPane.setBottomComponent(component);
268 }
269 }
270 else {
271 if (splitPane.getLeftComponent() == null) {
272 splitPane.setLeftComponent(component);
273 }
274 else {
275 splitPane.setRightComponent(component);
276 }
277 }
278 }
279 else if (parent instanceof JMenuBar && component instanceof JMenu) {
280 JMenuBar menuBar = (JMenuBar) parent;
281 menuBar.add((JMenu) component);
282 }
283 else if (parent instanceof Container) {
284 Container container = (Container) parent;
285 if (constraints != null) {
286 container.add(component, constraints);
287 }
288 else {
289 container.add(component);
290 }
291 }
292 else if (parent instanceof ContainerFacade) {
293 ContainerFacade facade = (ContainerFacade) parent;
294 facade.addComponent(component);
295 }
296 }
297 }
298 }
299
300 protected void nodeCompleted(Object parent, Object node) {
301 // set models after the node has been completed
302 if (node instanceof TableModel && parent instanceof JTable) {
303 JTable table = (JTable) parent;
304 TableModel model = (TableModel) node;
305 table.setModel(model);
306 }
307 if (node instanceof Startable) {
308 Startable startable = (Startable) node;
309 startable.start();
310 }
311 if (node instanceof Window) {
312 if (!containingWindows.isEmpty() && containingWindows.getLast() == node) {
313 containingWindows.removeLast();
314 }
315 }
316 }
317
318 protected Object createNode(Object name) {
319 return createNode(name, Collections.EMPTY_MAP);
320 }
321
322 protected Object createNode(Object name, Object value) {
323 if (passThroughNodes.containsKey(name) && (value != null) && ((Class)passThroughNodes.get(name)).isAssignableFrom(value.getClass())) {
324 // value may need to go into containing windows list
325 if (value instanceof Window) {
326 containingWindows.add(value);
327 }
328 return value;
329 }
330 else if (value instanceof String) {
331 Object widget = createNode(name);
332 if (widget != null) {
333 InvokerHelper.invokeMethod(widget, "setText", value);
334 }
335 return widget;
336 }
337 else {
338 throw new MissingMethodException((String) name, getClass(), new Object[] {value});
339 }
340 }
341
342 protected Object createNode(Object name, Map attributes, Object value) {
343 if (passThroughNodes.containsKey(name) && (value != null) && ((Class)passThroughNodes.get(name)).isAssignableFrom(value.getClass())) {
344 // value may need to go into containing windows list
345 if (value instanceof Window) {
346 containingWindows.add(value);
347 }
348 handleWidgetAttributes(value, attributes);
349 return value;
350 }
351 else {
352 Object widget = createNode(name, attributes);
353 if (widget != null) {
354 InvokerHelper.invokeMethod(widget, "setText", value.toString());
355 }
356 return widget;
357 }
358 }
359
360 protected Object createNode(Object name, Map attributes) {
361 String widgetName = (String) attributes.remove("id");
362 constraints = attributes.remove("constraints");
363 Object widget = null;
364 if (passThroughNodes.containsKey(name)) {
365 widget = attributes.get(name);
366 if ((widget != null) && ((Class)passThroughNodes.get(name)).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 }
373 else {
374 widget = null;
375 }
376 }
377 if (widget == null) {
378 Factory factory = (Factory) factories.get(name);
379 if (factory != null) {
380 try {
381 widget = factory.newInstance(attributes);
382 if (widgetName != null) {
383 widgets.put(widgetName, widget);
384 }
385 if (widget == null) {
386 log.log(Level.WARNING, "Factory for name: " + name + " returned null");
387 }
388 else {
389 if (log.isLoggable(Level.FINE)) {
390 log.fine("For name: " + name + " created widget: " + widget);
391 }
392 }
393 }
394 catch (Exception e) {
395 throw new RuntimeException("Failed to create component for" + name + " reason: " + e, e);
396 }
397 }
398 else {
399 log.log(Level.WARNING, "Could not find match for name: " + name);
400 }
401 }
402 handleWidgetAttributes(widget, attributes);
403 return widget;
404 }
405
406 protected void handleWidgetAttributes(Object widget, Map attributes) {
407 if (widget != null) {
408 if (widget instanceof Action) {
409 /** @todo we could move this custom logic into the MetaClass for Action */
410 Action action = (Action) widget;
411
412 Closure closure = (Closure) attributes.remove("closure");
413 if (closure != null && action instanceof DefaultAction) {
414 DefaultAction defaultAction = (DefaultAction) action;
415 defaultAction.setClosure(closure);
416 }
417
418 Object accel = attributes.remove("accelerator");
419 KeyStroke stroke = null;
420 if (accel instanceof KeyStroke) {
421 stroke = (KeyStroke) accel;
422 } else if (accel != null) {
423 stroke = KeyStroke.getKeyStroke(accel.toString());
424 }
425 action.putValue(Action.ACCELERATOR_KEY, stroke);
426
427 Object mnemonic = attributes.remove("mnemonic");
428 if ((mnemonic != null) && !(mnemonic instanceof Number)) {
429 mnemonic = new Integer(mnemonic.toString().charAt(0));
430 }
431 action.putValue(Action.MNEMONIC_KEY, mnemonic);
432
433 for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
434 Map.Entry entry = (Map.Entry) iter.next();
435 String actionName = (String) entry.getKey();
436
437 // typically standard Action names start with upper case, so lets upper case it
438 actionName = capitalize(actionName);
439 Object value = entry.getValue();
440
441 action.putValue(actionName, value);
442 }
443
444 }
445 else {
446 // some special cases...
447 if (attributes.containsKey("buttonGroup")) {
448 Object o = attributes.get("buttonGroup");
449 if ((o instanceof ButtonGroup) && (widget instanceof AbstractButton)) {
450 ((AbstractButton)widget).getModel().setGroup((ButtonGroup)o);
451 attributes.remove("buttonGroup");
452 }
453 }
454
455 // this next statement nd if/else is a workaround until GROOVY-305 is fixed
456 Object mnemonic = attributes.remove("mnemonic");
457 if ((mnemonic != null) && (mnemonic instanceof Number)) {
458 InvokerHelper.setProperty(widget, "mnemonic", new Character((char)((Number)mnemonic).intValue()));
459 }
460 else if (mnemonic != null) {
461 InvokerHelper.setProperty(widget, "mnemonic", new Character(mnemonic.toString().charAt(0)));
462 }
463
464 // set the properties
465 for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
466 Map.Entry entry = (Map.Entry) iter.next();
467 String property = entry.getKey().toString();
468 Object value = entry.getValue();
469 InvokerHelper.setProperty(widget, property, value);
470 }
471 }
472 }
473 }
474
475 protected String capitalize(String text) {
476 char ch = text.charAt(0);
477 if (Character.isUpperCase(ch)) {
478 return text;
479 }
480 StringBuffer buffer = new StringBuffer(text.length());
481 buffer.append(Character.toUpperCase(ch));
482 buffer.append(text.substring(1));
483 return buffer.toString();
484 }
485
486 protected void registerWidgets() {
487 //
488 // non-widget support classes
489 //
490 registerBeanFactory("action", DefaultAction.class);
491 passThroughNodes.put("action", javax.swing.Action.class);
492 registerBeanFactory("buttonGroup", ButtonGroup.class);
493 registerFactory("map", new Factory() {
494 public Object newInstance(Map properties)
495 throws InstantiationException, InstantiationException, IllegalAccessException {
496 return properties;
497 }
498 });
499 // ulimate pass through type
500 passThroughNodes.put("widget", java.awt.Component.class);
501
502 //
503 // standalone window classes
504 //
505 registerFactory("dialog", new Factory() {
506 public Object newInstance(Map properties)
507 throws InstantiationException, InstantiationException, IllegalAccessException {
508 return createDialog(properties);
509 }
510 });
511 registerFactory("frame", new Factory() {
512 public Object newInstance(Map properties)
513 throws InstantiationException, InstantiationException, IllegalAccessException {
514 return createFrame(properties);
515 }
516 });
517 registerBeanFactory("fileChooser", JFileChooser.class);
518 registerFactory("frame", new Factory() {
519 public Object newInstance(Map properties)
520 throws InstantiationException, InstantiationException, IllegalAccessException {
521 return createFrame(properties);
522 }
523 });
524 registerBeanFactory("optionPane", JOptionPane.class);
525 registerFactory("window", new Factory() {
526 public Object newInstance(Map properties)
527 throws InstantiationException, InstantiationException, IllegalAccessException {
528 return createWindow(properties);
529 }
530 });
531
532 //
533 // widgets
534 //
535 registerBeanFactory("button", JButton.class);
536 registerBeanFactory("checkBox", JCheckBox.class);
537 registerBeanFactory("checkBoxMenuItem", JCheckBoxMenuItem.class);
538 registerBeanFactory("colorChooser", JColorChooser.class);
539 registerFactory("comboBox", new Factory() {
540 public Object newInstance(Map properties)
541 throws InstantiationException, InstantiationException, IllegalAccessException {
542 return createComboBox(properties);
543 }
544 });
545 registerBeanFactory("desktopPane", JDesktopPane.class);
546 registerBeanFactory("editorPane", JEditorPane.class);
547 registerFactory("formattedTextField", new Factory() {
548 public Object newInstance(Map properties)
549 throws InstantiationException, InstantiationException, IllegalAccessException {
550 return createFormattedTextField(properties);
551 }
552 });
553 registerBeanFactory("internalFrame", JInternalFrame.class);
554 registerBeanFactory("label", JLabel.class);
555 registerBeanFactory("layeredPane", JLayeredPane.class);
556 registerBeanFactory("list", JList.class);
557 registerBeanFactory("menu", JMenu.class);
558 registerBeanFactory("menuBar", JMenuBar.class);
559 registerBeanFactory("menuItem", JMenuItem.class);
560 registerBeanFactory("panel", JPanel.class);
561 registerBeanFactory("passwordField", JPasswordField.class);
562 registerBeanFactory("popupMenu", JPopupMenu.class);
563 registerBeanFactory("progressBar", JProgressBar.class);
564 registerBeanFactory("radioButton", JRadioButton.class);
565 registerBeanFactory("radioButtonMenuItem", JRadioButtonMenuItem.class);
566 registerBeanFactory("scrollBar", JScrollBar.class);
567 registerBeanFactory("scrollPane", JScrollPane.class);
568 registerBeanFactory("separator", JSeparator.class);
569 registerBeanFactory("slider", JSlider.class);
570 registerBeanFactory("spinner", JSpinner.class);
571 registerFactory("splitPane", new Factory() {
572 public Object newInstance(Map properties) {
573 JSplitPane answer = new JSplitPane();
574 answer.setLeftComponent(null);
575 answer.setRightComponent(null);
576 answer.setTopComponent(null);
577 answer.setBottomComponent(null);
578 return answer;
579 }
580 });
581 registerBeanFactory("tabbedPane", JTabbedPane.class);
582 registerBeanFactory("table", JTable.class);
583 registerBeanFactory("textArea", JTextArea.class);
584 registerBeanFactory("textPane", JTextPane.class);
585 registerBeanFactory("textField", JTextField.class);
586 registerBeanFactory("toggleButton", JToggleButton.class);
587 registerBeanFactory("toolBar", JToolBar.class);
588 //registerBeanFactory("tooltip", JToolTip.class); // doens't work, user toolTipText property
589 registerBeanFactory("tree", JTree.class);
590 registerBeanFactory("viewport", JViewport.class); // sub class?
591
592 //
593 // MVC models
594 //
595 registerBeanFactory("boundedRangeModel", DefaultBoundedRangeModel.class);
596
597 // spinner models
598 registerBeanFactory("spinnerDateModel", SpinnerDateModel.class);
599 registerBeanFactory("spinnerListModel", SpinnerListModel.class);
600 registerBeanFactory("spinnerNumberModel", SpinnerNumberModel.class);
601
602 // table models
603 registerFactory("tableModel", new Factory() {
604 public Object newInstance(Map properties) {
605 ValueModel model = (ValueModel) properties.remove("model");
606 if (model == null) {
607 Object list = properties.remove("list");
608 if (list == null) {
609 list = new ArrayList();
610 }
611 model = new ValueHolder(list);
612 }
613 return new DefaultTableModel(model);
614 }
615 });
616 passThroughNodes.put("tableModel", javax.swing.table.TableModel.class);
617
618 registerFactory("propertyColumn", new Factory() {
619 public Object newInstance(Map properties) {
620 Object current = getCurrent();
621 if (current instanceof DefaultTableModel) {
622 DefaultTableModel model = (DefaultTableModel) current;
623 Object header = properties.remove("header");
624 if (header == null) {
625 header = "";
626 }
627 String property = (String) properties.remove("propertyName");
628 if (property == null) {
629 throw new IllegalArgumentException("Must specify a property for a propertyColumn");
630 }
631 Class type = (Class) properties.remove("type");
632 if (type == null) {
633 type = Object.class;
634 }
635 return model.addPropertyColumn(header, property, type);
636 }
637 else {
638 throw new RuntimeException("propertyColumn must be a child of a tableModel");
639 }
640 }
641 });
642
643 registerFactory("closureColumn", new Factory() {
644 public Object newInstance(Map properties) {
645 Object current = getCurrent();
646 if (current instanceof DefaultTableModel) {
647 DefaultTableModel model = (DefaultTableModel) current;
648 Object header = properties.remove("header");
649 if (header == null) {
650 header = "";
651 }
652 Closure readClosure = (Closure) properties.remove("read");
653 if (readClosure == null) {
654 throw new IllegalArgumentException("Must specify 'read' Closure property for a closureColumn");
655 }
656 Closure writeClosure = (Closure) properties.remove("write");
657 Class type = (Class) properties.remove("type");
658 if (type == null) {
659 type = Object.class;
660 }
661 return model.addClosureColumn(header, readClosure, writeClosure, type);
662 }
663 else {
664 throw new RuntimeException("propertyColumn must be a child of a tableModel");
665 }
666 }
667 });
668
669
670 //Standard Layouts
671 registerBeanFactory("borderLayout", BorderLayout.class);
672 registerBeanFactory("cardLayout", CardLayout.class);
673 registerBeanFactory("flowLayout", FlowLayout.class);
674 registerBeanFactory("gridBagLayout", GridBagLayout.class);
675 registerBeanFactory("gridLayout", GridLayout.class);
676 registerBeanFactory("overlayLayout", OverlayLayout.class);
677 registerBeanFactory("springLayout", SpringLayout.class);
678 registerBeanFactory("gridBagConstarints", GridBagConstraints.class);
679 registerBeanFactory("gbc", GridBagConstraints.class); // shortcut name
680
681 // box layout
682 registerFactory("boxLayout", new Factory() {
683 public Object newInstance(Map properties)
684 throws InstantiationException, InstantiationException, IllegalAccessException {
685 return createBoxLayout(properties);
686 }
687 });
688
689 // Box related layout components
690 registerFactory("hbox", new Factory() {
691 public Object newInstance(Map properties) {
692 return Box.createHorizontalBox();
693 }
694 });
695 registerFactory("hglue", new Factory() {
696 public Object newInstance(Map properties) {
697 return Box.createHorizontalGlue();
698 }
699 });
700 registerFactory("hstrut", new Factory() {
701 public Object newInstance(Map properties) {
702 try {
703 Object num = properties.remove("width");
704 if (num instanceof Number) {
705 return Box.createHorizontalStrut(((Number)num).intValue());
706 } else {
707 return Box.createHorizontalStrut(6);
708 }
709 } catch (RuntimeException re) {
710 re.printStackTrace(System.out);
711 throw re;
712 }
713 }
714 });
715 registerFactory("vbox", new Factory() {
716 public Object newInstance(Map properties) {
717 return Box.createVerticalBox();
718 }
719 });
720 registerFactory("vglue", new Factory() {
721 public Object newInstance(Map properties) {
722 return Box.createVerticalGlue();
723 }
724 });
725 registerFactory("vstrut", new Factory() {
726 public Object newInstance(Map properties) {
727 Object num = properties.remove("height");
728 if (num instanceof Number) {
729 return Box.createVerticalStrut(((Number)num).intValue());
730 } else {
731 return Box.createVerticalStrut(6);
732 }
733 }
734 });
735 registerFactory("glue", new Factory() {
736 public Object newInstance(Map properties) {
737 return Box.createGlue();
738 }
739 });
740 registerFactory("rigidArea", new Factory() {
741 public Object newInstance(Map properties) {
742 Dimension dim;
743 Object o = properties.remove("size");
744 if (o instanceof Dimension) {
745 dim = (Dimension) o;
746 } else {
747 int w, h;
748 o = properties.remove("width");
749 w = ((o instanceof Number)) ? ((Number)o).intValue() : 6;
750 o = properties.remove("height");
751 h = ((o instanceof Number)) ? ((Number)o).intValue() : 6;
752 dim = new Dimension(w, h);
753 }
754 return Box.createRigidArea(dim);
755 }
756 });
757
758 // table layout
759 registerBeanFactory("tableLayout", TableLayout.class);
760 registerFactory("tr", new Factory() {
761 public Object newInstance(Map properties) {
762 Object parent = getCurrent();
763 if (parent instanceof TableLayout) {
764 return new TableLayoutRow((TableLayout) parent);
765 }
766 else {
767 throw new RuntimeException("'tr' must be within a 'tableLayout'");
768 }
769 }
770 });
771 registerFactory("td", new Factory() {
772 public Object newInstance(Map properties) {
773 Object parent = getCurrent();
774 if (parent instanceof TableLayoutRow) {
775 return new TableLayoutCell((TableLayoutRow) parent);
776 }
777 else {
778 throw new RuntimeException("'td' must be within a 'tr'");
779 }
780 }
781 });
782 }
783
784 protected Object createBoxLayout(Map properties) {
785 Object parent = getCurrent();
786 if (parent instanceof Container) {
787 Object axisObject = properties.remove("axis");
788 int axis = 0;
789 if (axisObject != null) {
790 Integer i = (Integer) axisObject;
791 axis = i.intValue();
792 }
793 BoxLayout answer = new BoxLayout((Container) parent, axis);
794
795 // now lets try set the layout property
796 InvokerHelper.setProperty(parent, "layout", answer);
797 return answer;
798 }
799 else {
800 throw new RuntimeException("Must be nested inside a Container");
801 }
802 }
803
804 protected Object createDialog(Map properties) {
805 JDialog dialog;
806 Object owner = properties.remove("owner");
807 // if owner not explicit, use the last window type in the list
808 if ((owner == null) && !containingWindows.isEmpty()) {
809 owner = containingWindows.getLast();
810 }
811 if (owner instanceof Frame) {
812 dialog = new JDialog((Frame) owner);
813 }
814 else if (owner instanceof Dialog) {
815 dialog = new JDialog((Dialog) owner);
816 }
817 else {
818 dialog = new JDialog();
819 }
820 containingWindows.add(dialog);
821 return dialog;
822 }
823
824 /**
825 * Uses 'format," or "value," (in order)
826 *
827 */
828 protected Object createFormattedTextField(Map properties) {
829 JFormattedTextField ftf;
830 if (properties.containsKey("format")) {
831 ftf = new JFormattedTextField((Format) properties.remove("format"));
832 }
833 else if (properties.containsKey("value")) {
834 ftf = new JFormattedTextField(properties.remove("value"));
835 }
836 else {
837 ftf = new JFormattedTextField();
838 }
839 return ftf;
840 }
841
842 protected Object createFrame(Map properties) {
843 JFrame frame = new JFrame();
844 containingWindows.add(frame);
845 return frame;
846 }
847
848 protected Object createWindow(Map properties) {
849 JWindow window;
850 Object owner = properties.remove("owner");
851 // if owner not explicit, use the last window type in the list
852 if ((owner == null) && !containingWindows.isEmpty()) {
853 owner = containingWindows.getLast();
854 }
855 if (owner instanceof Frame) {
856 window = new JWindow((Frame) owner);
857 }
858 else if (owner instanceof Window) {
859 window = new JWindow((Window) owner);
860 }
861 else {
862 window = new JWindow();
863 }
864 containingWindows.add(window);
865 return window;
866 }
867
868 protected Object createComboBox(Map properties) {
869 Object items = properties.remove("items");
870 if (items instanceof Vector) {
871 return new JComboBox((Vector) items);
872 }
873 else if (items instanceof List) {
874 List list = (List) items;
875 return new JComboBox(list.toArray());
876 }
877 else if (items instanceof Object[]) {
878 return new JComboBox((Object[]) items);
879 }
880 else {
881 return new JComboBox();
882 }
883 }
884
885 protected void registerBeanFactory(String name, final Class beanClass) {
886 registerFactory(name, new Factory() {
887 public Object newInstance(Map properties) throws InstantiationException, IllegalAccessException {
888 return beanClass.newInstance();
889 }
890 });
891
892 }
893
894 protected void registerFactory(String name, Factory factory) {
895 factories.put(name, factory);
896 }
897 }