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