groovy.swing.SwingBuilder.groovy Maven / Gradle / Ivy
Show all versions of groovy-all Show documentation
/*
* Copyright 2003-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package groovy.swing
import groovy.swing.factory.*
import java.awt.*
import java.lang.reflect.InvocationTargetException
import java.util.logging.Logger
import javax.swing.*
import javax.swing.border.BevelBorder
import javax.swing.border.EtchedBorder
import javax.swing.table.TableColumn
import org.codehaus.groovy.runtime.MethodClosure
/**
* A helper class for creating Swing widgets using GroovyMarkup
*
* @author James Strachan
* @version $Revision: 16337 $
*/
public class SwingBuilder extends FactoryBuilderSupport {
// local fields
private static final Logger LOG = Logger.getLogger(SwingBuilder.name)
private static boolean headless = false
public static final String DELEGATE_PROPERTY_OBJECT_ID = "_delegateProperty:id";
public static final String DEFAULT_DELEGATE_PROPERTY_OBJECT_ID = "id";
private static final Random random = new Random()
public SwingBuilder(boolean init = true) {
super(init)
//registerWidgets()
headless = GraphicsEnvironment.isHeadless()
containingWindows = new LinkedList()
this[DELEGATE_PROPERTY_OBJECT_ID] = DEFAULT_DELEGATE_PROPERTY_OBJECT_ID
}
def registerSupportNodes() {
registerFactory("action", new ActionFactory())
registerFactory("actions", new CollectionFactory())
registerFactory("map", new MapFactory())
registerFactory("imageIcon", new ImageIconFactory())
registerFactory("buttonGroup", new ButtonGroupFactory())
addAttributeDelegate(ButtonGroupFactory.&buttonGroupAttributeDelegate)
//object id delegage, for propertyNotFound
addAttributeDelegate(SwingBuilder.&objectIDAttributeDelegate)
addAttributeDelegate(SwingBuilder.&clientPropertyAttributeDelegate)
registerFactory("noparent", new CollectionFactory())
registerExplicitMethod("keyStrokeAction", this.&createKeyStrokeAction)
registerExplicitMethod("shortcut", this.&shortcut)
}
def registerBinding() {
BindFactory bindFactory = new BindFactory()
registerFactory("bind", bindFactory)
addAttributeDelegate(bindFactory.&bindingAttributeDelegate)
registerFactory("bindProxy", new BindProxyFactory())
registerFactory ("bindGroup", new BindGroupFactory());
}
def registerPassThruNodes() {
registerFactory("widget", new WidgetFactory(Component, true))
registerFactory("container", new WidgetFactory(Component, false))
registerFactory("bean", new WidgetFactory(Object, true))
}
def registerWindows() {
registerFactory("dialog", new DialogFactory())
registerBeanFactory("fileChooser", JFileChooser)
registerFactory("frame", new FrameFactory())
registerBeanFactory("optionPane", JOptionPane)
registerFactory("window", new WindowFactory())
}
def registerActionButtonWidgets() {
registerFactory("button", new RichActionWidgetFactory(JButton))
registerFactory("checkBox", new RichActionWidgetFactory(JCheckBox))
registerFactory("checkBoxMenuItem", new RichActionWidgetFactory(JCheckBoxMenuItem))
registerFactory("menuItem", new RichActionWidgetFactory(JMenuItem))
registerFactory("radioButton", new RichActionWidgetFactory(JRadioButton))
registerFactory("radioButtonMenuItem", new RichActionWidgetFactory(JRadioButtonMenuItem))
registerFactory("toggleButton", new RichActionWidgetFactory(JToggleButton))
}
def registerTextWidgets() {
registerFactory("editorPane", new TextArgWidgetFactory(JEditorPane))
registerFactory("label", new TextArgWidgetFactory(JLabel))
registerFactory("passwordField", new TextArgWidgetFactory(JPasswordField))
registerFactory("textArea", new TextArgWidgetFactory(JTextArea))
registerFactory("textField", new TextArgWidgetFactory(JTextField))
registerFactory("formattedTextField", new FormattedTextFactory())
registerFactory("textPane", new TextArgWidgetFactory(JTextPane))
}
def registerMDIWidgets() {
registerBeanFactory("desktopPane", JDesktopPane)
registerFactory("internalFrame", new InternalFrameFactory())
}
def registerBasicWidgets() {
registerBeanFactory("colorChooser", JColorChooser)
registerFactory("comboBox", new ComboBoxFactory())
registerFactory("list", new ListFactory())
registerBeanFactory("progressBar", JProgressBar)
registerFactory("separator", new SeparatorFactory())
registerBeanFactory("scrollBar", JScrollBar)
registerBeanFactory("slider", JSlider)
registerBeanFactory("spinner", JSpinner)
registerBeanFactory("tree", JTree)
}
def registerMenuWidgets() {
registerBeanFactory("menu", JMenu)
registerBeanFactory("menuBar", JMenuBar)
registerBeanFactory("popupMenu", JPopupMenu)
}
def registerContainers() {
registerBeanFactory("panel", JPanel)
registerFactory("scrollPane", new ScrollPaneFactory())
registerFactory("splitPane", new SplitPaneFactory())
registerFactory("tabbedPane", new TabbedPaneFactory(JTabbedPane))
registerBeanFactory("toolBar", JToolBar)
registerBeanFactory("viewport", JViewport) // sub class?
registerBeanFactory("layeredPane", JLayeredPane)
}
def registerDataModels() {
registerBeanFactory("boundedRangeModel", DefaultBoundedRangeModel)
// spinner models
registerBeanFactory("spinnerDateModel", SpinnerDateModel)
registerBeanFactory("spinnerListModel", SpinnerListModel)
registerBeanFactory("spinnerNumberModel", SpinnerNumberModel)
}
def registerTableComponents() {
registerFactory("table", new TableFactory())
registerBeanFactory("tableColumn", TableColumn)
registerFactory("tableModel", new TableModelFactory())
registerFactory("propertyColumn", new PropertyColumnFactory())
registerFactory("closureColumn", new ClosureColumnFactory())
}
def registerBasicLayouts() {
registerFactory("borderLayout", new LayoutFactory(BorderLayout))
registerFactory("cardLayout", new LayoutFactory(CardLayout))
registerFactory("flowLayout", new LayoutFactory(FlowLayout))
registerFactory("gridLayout", new LayoutFactory(GridLayout))
registerFactory("overlayLayout", new LayoutFactory(OverlayLayout))
registerFactory("springLayout", new LayoutFactory(SpringLayout))
registerFactory("gridBagLayout", new GridBagFactory())
registerBeanFactory("gridBagConstraints", GridBagConstraints)
registerBeanFactory("gbc", GridBagConstraints) // shortcut name
// constraints delegate
addAttributeDelegate(GridBagFactory.&processGridBagConstraintsAttributes)
addAttributeDelegate(LayoutFactory.&constraintsAttributeDelegate)
}
def registerBoxLayout() {
registerFactory("boxLayout", new BoxLayoutFactory())
registerFactory("box", new BoxFactory())
registerFactory("hbox", new HBoxFactory())
registerFactory("hglue", new HGlueFactory())
registerFactory("hstrut", new HStrutFactory())
registerFactory("vbox", new VBoxFactory())
registerFactory("vglue", new VGlueFactory())
registerFactory("vstrut", new VStrutFactory())
registerFactory("glue", new GlueFactory())
registerFactory("rigidArea", new RigidAreaFactory())
}
def registerTableLayout() {
registerFactory("tableLayout", new TableLayoutFactory())
registerFactory("tr", new TRFactory())
registerFactory("td", new TDFactory())
}
def registerBorders() {
registerFactory("lineBorder", new LineBorderFactory())
registerFactory("loweredBevelBorder", new BevelBorderFactory(BevelBorder.LOWERED))
registerFactory("raisedBevelBorder", new BevelBorderFactory(BevelBorder.RAISED))
registerFactory("etchedBorder", new EtchedBorderFactory(EtchedBorder.LOWERED))
registerFactory("loweredEtchedBorder", new EtchedBorderFactory(EtchedBorder.LOWERED))
registerFactory("raisedEtchedBorder", new EtchedBorderFactory(EtchedBorder.RAISED))
registerFactory("titledBorder", new TitledBorderFactory())
registerFactory("emptyBorder", new EmptyBorderFactory())
registerFactory("compoundBorder", new CompoundBorderFactory())
registerFactory("matteBorder", new MatteBorderFactory())
}
def registerRenderers() {
RendererFactory renderFactory = new RendererFactory()
registerFactory("tableCellRenderer", renderFactory)
registerFactory("listCellRenderer", renderFactory)
registerFactory("onRender", new RendererUpdateFactory())
}
def registerThreading() {
registerExplicitMethod "edt", this.&edt
registerExplicitMethod "doOutside", this.&doOutside
registerExplicitMethod "doLater", this.&doLater
}
/**
* Do some overrides for standard component handlers, else use super
*/
public void registerBeanFactory(String nodeName, String groupName, Class klass) {
// poke at the type to see if we need special handling
if (LayoutManager.isAssignableFrom(klass)) {
registerFactory(nodeName, groupName, new LayoutFactory(klass))
} else if (JScrollPane.isAssignableFrom(klass)) {
registerFactory(nodeName, groupName, new ScrollPaneFactory(klass))
} else if (JTable.isAssignableFrom(klass)) {
registerFactory(nodeName, groupName, new TableFactory(klass))
} else if (JComponent.isAssignableFrom(klass)
|| JApplet.isAssignableFrom(klass)
|| JDialog.isAssignableFrom(klass)
|| JFrame.isAssignableFrom(klass)
|| JWindow.isAssignableFrom(klass)
) {
registerFactory(nodeName, groupName, new ComponentFactory(klass))
} else {
super.registerBeanFactory(nodeName, groupName, klass)
}
}
/**
* Utilitiy method to run a closure in EDT,
* using SwingUtilities.invokeAndWait.
*
* @param c this closure is run in the EDT
*/
public SwingBuilder edt(Closure c) {
c.setDelegate(this)
if (headless || SwingUtilities.isEventDispatchThread()) {
c.call(this)
} else {
Map continuationData = getContinuationData();
try {
if (!(c instanceof MethodClosure)) {
c = c.curry([this])
}
SwingUtilities.invokeAndWait {
restoreFromContinuationData(continuationData)
c()
continuationData = getContinuationData()
}
} catch (InterruptedException e) {
throw new GroovyRuntimeException("interrupted swing interaction", e)
} catch (InvocationTargetException e) {
throw new GroovyRuntimeException("exception in event dispatch thread", e.getTargetException())
} finally {
restoreFromContinuationData(continuationData);
}
}
return this
}
/**
* Utility method to run a closure in EDT,
* using SwingUtilities.invokeLater.
*
* @param c this closure is run in the EDT
*/
public SwingBuilder doLater(Closure c) {
c.setDelegate(this)
if (headless) {
c.call()
} else {
if (!(c instanceof MethodClosure)) {
c = c.curry([this])
}
SwingUtilities.invokeLater(c)
}
return this
}
/**
* Utility method to run a closure in a separate Thread.
*
* The closure is wrapped in a thread, and the thread is started
* immediatly.
*
* @param c this closure is started in a separate thread
*/
public SwingBuilder doOutside(Closure c) {
c.setDelegate(this)
if (!(c instanceof MethodClosure)) {
c = c.curry([this])
}
Thread.start(c)
return this
}
/**
* Factory method to create a SwingBuilder, and run the
* the closure in it on the EDT
*
* @param c run this closure in the new builder using the edt method
*/
public static SwingBuilder edtBuilder(Closure c) {
SwingBuilder builder = new SwingBuilder()
return builder.edt(c)
}
/**
* Old factory method static SwingBuilder.build(Closure).
* @param c run this closure in the builder using the edt method
*/
@Deprecated
public static SwingBuilder '$static_methodMissing'(String method, Object args) {
if (method == 'build' && args.length == 1 && args[0] instanceof Closure) {
return edtBuilder(args[0])
} else {
throw new MissingMethodException(method, SwingBuilder, args, true)
}
}
/**
* Compatibility API.
*
* @param c run this closure in the builder
*/
public Object build(Closure c) {
c.setDelegate(this)
return c.call()
}
public KeyStroke shortcut(key, modifier = 0) {
return KeyStroke.getKeyStroke(key, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | modifier)
}
public KeyStroke shortcut(String key, modifier = 0) {
KeyStroke ks = KeyStroke.getKeyStroke(key)
if (ks == null) {
return null
} else {
return KeyStroke.getKeyStroke(ks.getKeyCode(), ks.getModifiers() | modifier | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) }
}
public static LookAndFeel lookAndFeel(Object laf, Closure initCode) {
lookAndFeel([:], laf, initCode)
}
public static LookAndFeel lookAndFeel(Map attributes = [:], Object laf = null, Closure initCode = null) {
// if we get rid of this warning, we can make it static.
//if (context) {
// LOG.warning "For best result do not call lookAndFeel when it is a child of a SwingBuidler node, initializaiton of the Look and Feel may be inconsistant."
//}
LookAndFeelHelper.instance.lookAndFeel(laf, attributes, initCode)
}
public static LookAndFeel lookAndFeel(Object... lafs) {
if (lafs.length == 1) {
lookAndFeel([:], lafs[0], null as Closure);
}
for (Object laf in lafs) {
try {
// (ab)use multi-methods
if (laf instanceof ArrayList) {
// multi-method bug
return _laf(*laf)
} else {
return _laf(laf)
}
} catch (Throwable t) {
LOG.fine "Could not instantiate Look and Feel $laf because of ${t}. Attemting next option."
}
}
LOG.warning "All Look and Feel options failed: $lafs"
return null
}
private static LookAndFeel _laf(java.util.List s) {
_laf(*s)
}
private static LookAndFeel _laf(String s, Map m) {
lookAndFeel(m, s, null as Closure)
}
private static LookAndFeel _laf(LookAndFeel laf, Map m) {
lookAndFeel(m, laf, null as Closure)
}
private static LookAndFeel _laf(String s) {
lookAndFeel([:], s, null as Closure)
}
private static LookAndFeel _laf(LookAndFeel laf) {
lookAndFeel([:], laf, null as Closure)
}
public static objectIDAttributeDelegate(def builder, def node, def attributes) {
def idAttr = builder.getAt(DELEGATE_PROPERTY_OBJECT_ID) ?: DEFAULT_DELEGATE_PROPERTY_OBJECT_ID
def theID = attributes.remove(idAttr)
if (theID) {
builder.setVariable(theID, node)
}
}
public static clientPropertyAttributeDelegate(def builder, def node, def attributes) {
def clientPropertyMap = attributes.remove("clientProperties")
clientPropertyMap.each { key, value ->
node.putClientProperty key, value
}
attributes.findAll { it.key =~ /clientProperty(\w)/ }.each { key, value ->
attributes.remove(key)
node.putClientProperty(key - "clientProperty", value)
}
}
public void createKeyStrokeAction( Map attributes, JComponent component = null ) {
component = findTargetComponent(attributes, component)
if( !attributes.containsKey("keyStroke") ) {
throw new RuntimeException("You must define a value for keyStroke:")
}
if( !attributes.containsKey("action") ) {
throw new RuntimeException("You must define a value for action:")
}
def condition = attributes.remove("condition") ?: JComponent.WHEN_FOCUSED
if( condition instanceof String ) {
condition = condition.toUpperCase().replaceAll(" ","_")
if( !condition.startsWith("WHEN_") ) condition = "WHEN_"+condition
}
switch(condition) {
case JComponent.WHEN_FOCUSED:
case JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT:
case JComponent.WHEN_IN_FOCUSED_WINDOW:
// everything is fine, no further processing
break
case "WHEN_FOCUSED":
condition = JComponent.WHEN_FOCUSED
break
case "WHEN_ANCESTOR_OF_FOCUSED_COMPONENT":
condition = JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
break
case "WHEN_IN_FOCUSED_WINDOW":
condition = JComponent.WHEN_IN_FOCUSED_WINDOW
break
default:
// let's be lenient and asign WHEN_FOCUSED by default
condition = JComponent.WHEN_FOCUSED
}
def actionKey = attributes.remove("actionKey")
if( !actionKey ) actionKey = "Action"+Math.abs(random.nextLong())
def keyStroke = attributes.remove("keyStroke")
// accept String, KeyStroke, List, List
def action = attributes.remove("action")
if( keyStroke instanceof String ) keyStroke = [keyStroke]
keyStroke.each { ks ->
switch(ks) {
case KeyStroke:
component.getInputMap(condition).put(ks, actionKey)
break
case String:
component.getInputMap(condition).put(shortcut(ks), actionKey)
break
default:
throw new RuntimeException("Cannot apply ${ks} as a KeyStroke value.")
}
}
component.actionMap.put(actionKey, action)
}
private findTargetComponent( Map attributes, JComponent component ) {
if( component ) return component
if( attributes.containsKey("component") ) {
def c = attributes.remove("component")
if( !(c instanceof JComponent) ) {
throw new RuntimeException("The property component: is not of type JComponent.")
}
return c
}
def c = getCurrent()
if( c instanceof JComponent ) {
return c
}
throw new RuntimeException("You must define one of the following: a value of type JComponent, a component: attribute or nest this node inside another one that produces a JComponent.")
}
}