001 /* 002 $Id: SwingBuilder.java,v 1.17 2005/10/25 12:32:11 alang 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.17 $ 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}); 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 }