Java tutorial
/******************************************************************************* * Copyright (c) 2011 Google, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Google, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.wb.internal.swt.model.layout.absolute; import org.eclipse.wb.core.editor.IContextMenuConstants; import org.eclipse.wb.core.model.JavaInfo; import org.eclipse.wb.core.model.ObjectInfo; import org.eclipse.wb.core.model.ObjectInfoUtils; import org.eclipse.wb.core.model.broadcast.JavaEventListener; import org.eclipse.wb.core.model.broadcast.JavaInfoAddProperties; import org.eclipse.wb.core.model.broadcast.ObjectEventListener; import org.eclipse.wb.draw2d.geometry.Dimension; import org.eclipse.wb.draw2d.geometry.Point; import org.eclipse.wb.draw2d.geometry.Rectangle; import org.eclipse.wb.internal.core.DesignerPlugin; import org.eclipse.wb.internal.core.model.JavaInfoUtils; import org.eclipse.wb.internal.core.model.clipboard.ClipboardCommand; import org.eclipse.wb.internal.core.model.creation.ConstructorCreationSupport; import org.eclipse.wb.internal.core.model.creation.CreationSupport; import org.eclipse.wb.internal.core.model.description.ComponentDescription; import org.eclipse.wb.internal.core.model.description.GenericPropertyDescription; import org.eclipse.wb.internal.core.model.description.ToolkitDescription; import org.eclipse.wb.internal.core.model.layout.absolute.BoundsProperty; import org.eclipse.wb.internal.core.model.layout.absolute.IPreferenceConstants; import org.eclipse.wb.internal.core.model.layout.absolute.OrderingSupport; import org.eclipse.wb.internal.core.model.property.ComplexProperty; import org.eclipse.wb.internal.core.model.property.GenericPropertyImpl; import org.eclipse.wb.internal.core.model.property.Property; import org.eclipse.wb.internal.core.model.property.category.PropertyCategory; import org.eclipse.wb.internal.core.model.property.converter.IntegerConverter; import org.eclipse.wb.internal.core.model.util.AbsoluteLayoutCreationFlowSupport; import org.eclipse.wb.internal.core.model.util.ObjectInfoAction; import org.eclipse.wb.internal.core.utils.ast.AstEditor; import org.eclipse.wb.internal.core.utils.check.Assert; import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils; import org.eclipse.wb.internal.core.utils.execution.RunnableEx; import org.eclipse.wb.internal.swt.Activator; import org.eclipse.wb.internal.swt.model.ModelMessages; import org.eclipse.wb.internal.swt.model.layout.LayoutClipboardCommand; import org.eclipse.wb.internal.swt.model.layout.LayoutInfo; import org.eclipse.wb.internal.swt.model.widgets.CompositeInfo; import org.eclipse.wb.internal.swt.model.widgets.ControlInfo; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.swt.layout.FormLayout; import java.util.Collections; import java.util.List; /** * Model for "null" (absolute) layout. * * @author mitin_aa * @author scheglov_ke * @author lobas_av * @coverage swt.model.layout */ public final class AbsoluteLayoutInfo extends LayoutInfo implements IAbsoluteLayoutInfo<ControlInfo> { // method names private static final String SET_SIZE_POINT = "setSize(org.eclipse.swt.graphics.Point)"; private static final String SET_SIZE_INT_INT = "setSize(int,int)"; private static final String SET_LOCATION_POINT = "setLocation(org.eclipse.swt.graphics.Point)"; private static final String SET_LOCATION_INT_INT = "setLocation(int,int)"; //////////////////////////////////////////////////////////////////////////// // // Constructor // //////////////////////////////////////////////////////////////////////////// public AbsoluteLayoutInfo(AstEditor editor, ToolkitDescription toolkit, CreationSupport creationSupport) throws Exception { super(editor, new ComponentDescription(null), creationSupport); ObjectInfoUtils.setNewId(this); getDescription().setToolkit(toolkit); getDescription().setOrder("first"); getDescription().setIcon(Activator.getImage("info/layout/absolute/layout.gif")); setVariableSupport(new AbsoluteLayoutVariableSupport(this)); } //////////////////////////////////////////////////////////////////////////// // // Initialization // //////////////////////////////////////////////////////////////////////////// @Override protected void initialize() throws Exception { super.initialize(); whenSetComplexLayout_forChildComposite(); // auto size addBroadcastListener(new JavaEventListener() { @Override public void setPropertyExpression(GenericPropertyImpl property, String[] source, Object[] value, boolean[] shouldSet) throws Exception { checkForAutoSize(property); } private void checkForAutoSize(GenericPropertyImpl property) throws Exception { IPreferenceStore preferences = getDescription().getToolkit().getPreferences(); if (preferences.getBoolean(IPreferenceConstants.P_AUTOSIZE_ON_PROPERTY_CHANGE) && isManagedObject(property.getJavaInfo())) { GenericPropertyDescription propertyDescription = property.getDescription(); if (propertyDescription != null) { boolean isTextProperty = propertyDescription.hasTrueTag("isText"); boolean isImageProperty = propertyDescription.hasTrueTag("isImage"); if (isTextProperty || isImageProperty) { // schedule auto-size final ControlInfo control = (ControlInfo) property.getJavaInfo(); ExecutionUtils.runLater(control, new RunnableEx() { public void run() throws Exception { commandChangeBounds(control, null, control.getPreferredSize()); } }); } } } } }); // Bounds property addBroadcastListener(new JavaInfoAddProperties() { public void invoke(JavaInfo javaInfo, List<Property> properties) throws Exception { if (isManagedObject(javaInfo)) { ControlInfo control = (ControlInfo) javaInfo; properties.add(getBoundsProperty(control)); } } }); addBroadcastListener(new JavaEventListener() { @Override public void canMove(JavaInfo javaInfo, boolean[] forceMoveEnable, boolean[] forceMoveDisable) throws Exception { if (isManagedObject(javaInfo)) { forceMoveEnable[0] = true; } } }); // context menu addBroadcastListener(new ObjectEventListener() { @Override public void addContextMenu(List<? extends ObjectInfo> objects, ObjectInfo object, IMenuManager manager) throws Exception { if (isManagedObject(object)) { ControlInfo control = (ControlInfo) object; contributeControlContextMenu(manager, control); } } }); } /** * Absolute layout is place when layout stops. So, even while at design time we show correct GUI, * at run time complex layout of child will not work without special tricks. So, we ask user to * use {@link FormLayout} instead on "absolute". */ private void whenSetComplexLayout_forChildComposite() { addBroadcastListener(new JavaEventListener() { @Override public void addAfter(JavaInfo parent, JavaInfo child) throws Exception { if (child instanceof LayoutInfo && !(child instanceof AbsoluteLayoutInfo) && parent instanceof CompositeInfo && parent.getParent() == getComposite()) { int result = openQuestion(); if (result == 0) { LayoutInfo formLayout = (LayoutInfo) JavaInfoUtils.createJavaInfo(getEditor(), "org.eclipse.swt.layout.FormLayout", new ConstructorCreationSupport()); getComposite().setLayout(formLayout); } } } private int openQuestion() { MessageDialog dialog = new MessageDialog(DesignerPlugin.getShell(), ModelMessages.AbsoluteLayoutInfo_layoutConfirmTitle, null, ModelMessages.AbsoluteLayoutInfo_layoutConfirmMessage1 + ModelMessages.AbsoluteLayoutInfo_layoutConfirmMessage2 + ModelMessages.AbsoluteLayoutInfo_layoutConfirmMessage3 + ModelMessages.AbsoluteLayoutInfo_layoutConfirmMessage4 + ModelMessages.AbsoluteLayoutInfo_layoutConfirmMessage5, MessageDialog.WARNING, new String[] { ModelMessages.AbsoluteLayoutInfo_layoutConfirmButtonYes, ModelMessages.AbsoluteLayoutInfo_layoutConfirmButtonNo }, 0); return dialog.open(); } }); } //////////////////////////////////////////////////////////////////////////// // // Context menu // //////////////////////////////////////////////////////////////////////////// /** * Contributes {@link Action}'s into {@link ControlInfo} context menu. */ private void contributeControlContextMenu(IMenuManager manager, final ControlInfo control) { // order { List<ControlInfo> controls = getComposite().getChildrenControls(); new OrderingSupport(controls, control).contributeActions(manager); } // auto-size { IAction action = new ObjectInfoAction(control, ModelMessages.AbsoluteLayoutInfo_autoSize, DesignerPlugin.getImageDescriptor("info/layout/absolute/fit_to_size.png")) { @Override protected void runEx() throws Exception { commandChangeBounds(control, null, control.getPreferredSize()); } }; manager.appendToGroup(IContextMenuConstants.GROUP_CONSTRAINTS, action); } } //////////////////////////////////////////////////////////////////////////// // // Conversion // //////////////////////////////////////////////////////////////////////////// @Override public void onSet() throws Exception { for (ControlInfo control : getComposite().getChildrenControls()) { Rectangle bounds = control.getModelBounds(); control.addMethodInvocation("setBounds(int,int,int,int)", bounds.x + ", " + bounds.y + ", " + bounds.width + ", " + bounds.height); } } @Override protected void deleteLayoutData(ControlInfo control) throws Exception { super.deleteLayoutData(control); control.removeMethodInvocations(SET_LOCATION_INT_INT); control.removeMethodInvocations(SET_LOCATION_POINT); control.removeMethodInvocations(SET_SIZE_INT_INT); control.removeMethodInvocations(SET_SIZE_POINT); control.removeMethodInvocations("setBounds(int,int,int,int)"); control.removeMethodInvocations("setBounds(org.eclipse.swt.graphics.Rectangle)"); } //////////////////////////////////////////////////////////////////////////// // // Properties // //////////////////////////////////////////////////////////////////////////// @Override protected List<Property> getPropertyList() throws Exception { return Collections.emptyList(); } //////////////////////////////////////////////////////////////////////////// // // Bounds Property // //////////////////////////////////////////////////////////////////////////// private Property getBoundsProperty(ControlInfo control) { ComplexProperty boundsProperty = (ComplexProperty) control.getArbitraryValue(this); if (boundsProperty == null) { boundsProperty = new ComplexProperty("Bounds", null); boundsProperty.setCategory(PropertyCategory.system(5)); boundsProperty.setModified(true); control.putArbitraryValue(this, boundsProperty); // x BoundsProperty<?> xProperty = new BoundsProperty<ControlInfo>(control, "x") { @Override public void setValue2(int value, Rectangle modelBounds) throws Exception { commandChangeBounds(m_component, new Point(value, modelBounds.y), null); } }; // y BoundsProperty<?> yProperty = new BoundsProperty<ControlInfo>(control, "y") { @Override public void setValue2(int value, Rectangle modelBounds) throws Exception { commandChangeBounds(m_component, new Point(modelBounds.x, value), null); } }; // width BoundsProperty<?> widthProperty = new BoundsProperty<ControlInfo>(control, "width") { @Override public void setValue2(int value, Rectangle modelBounds) throws Exception { commandChangeBounds(m_component, null, new Dimension(value, modelBounds.height)); } }; // height BoundsProperty<?> heightProperty = new BoundsProperty<ControlInfo>(control, "height") { @Override public void setValue2(int value, Rectangle modelBounds) throws Exception { commandChangeBounds(m_component, null, new Dimension(modelBounds.width, value)); } }; boundsProperty.setProperties(new Property[] { xProperty, yProperty, widthProperty, heightProperty }); } Rectangle modelBounds = control.getModelBounds(); if (modelBounds != null) { boundsProperty.setText("(" + modelBounds.x + ", " + modelBounds.y + ", " + modelBounds.width + ", " + modelBounds.height + ")"); } return boundsProperty; } //////////////////////////////////////////////////////////////////////////// // // Commands // //////////////////////////////////////////////////////////////////////////// public void commandCreate(ControlInfo control, ControlInfo nextControl) throws Exception { command_CREATE(control, nextControl); } public void commandMove(ControlInfo control, ControlInfo nextControl) throws Exception { command_MOVE(control, nextControl); } public void commandChangeBounds(ControlInfo widget, Point location, Dimension size) throws Exception { commandChangeBounds0(widget, location, size); // apply creation flow if (location != null && useCreationFlow()) { AbsoluteLayoutCreationFlowSupport.apply(getComposite(), getControls(), widget, location, size); } } private void commandChangeBounds0(ControlInfo widget, Point location, Dimension size) throws Exception { Assert.isLegal(location != null || size != null, ModelMessages.AbsoluteLayoutInfo_notNullLocationSize); AstEditor editor = widget.getEditor(); // setBounds(int,int,int,int) { MethodInvocation mi = widget.getMethodInvocation("setBounds(int,int,int,int)"); if (mi != null) { if (location != null) { setExpression(mi, 0, location.x); setExpression(mi, 1, location.y); } if (size != null) { setExpression(mi, 2, size.width); setExpression(mi, 3, size.height); } removeUnusedMethodsAfterSetBounds(widget); return; } } // setBounds(org.eclipse.swt.graphics.Rectangle) { MethodInvocation mi = widget.getMethodInvocation("setBounds(org.eclipse.swt.graphics.Rectangle)"); if (mi != null) { Rectangle widgetBounds = widget.getModelBounds(); Expression exp = (Expression) mi.arguments().get(0); String newCode = "new org.eclipse.swt.graphics.Rectangle("; if (location != null) { newCode += location.x + ", " + location.y; } else { newCode += widgetBounds.x + ", " + widgetBounds.y; } newCode += ", "; if (size != null) { newCode += size.width + ", " + size.height; } else { newCode += widgetBounds.width + ", " + widgetBounds.height; } newCode += ")"; editor.replaceExpression(exp, newCode); removeUnusedMethodsAfterSetBounds(widget); return; } } boolean setLocationFound = false; boolean setSizeFound = false; if (location != null) { // setLocation(int,int) { MethodInvocation mi = widget.getMethodInvocation(SET_LOCATION_INT_INT); if (mi != null) { setLocationFound = true; setExpression(mi, 0, location.x); setExpression(mi, 1, location.y); } } // setLocation(new org.eclipse.swt.graphics.Point) { MethodInvocation mi = widget.getMethodInvocation(SET_LOCATION_POINT); if (mi != null) { setLocationFound = true; Expression exp = (Expression) mi.arguments().get(0); String newCode = "new org.eclipse.swt.graphics.Point("; newCode += location.x + ", " + location.y + ")"; editor.replaceExpression(exp, newCode); } } } if (size != null) { // setSize(int,int) { MethodInvocation mi = widget.getMethodInvocation(SET_SIZE_INT_INT); if (mi != null) { setSizeFound = true; setExpression(mi, 0, size.width); setExpression(mi, 1, size.height); } } // setSize(new org.eclipse.swt.graphics.Point) { MethodInvocation mi = widget.getMethodInvocation(SET_SIZE_POINT); if (mi != null) { setSizeFound = true; Expression exp = (Expression) mi.arguments().get(0); String newCode = "new org.eclipse.swt.graphics.Point("; newCode += size.width + ", " + size.height + ")"; editor.replaceExpression(exp, newCode); } } } // nothing found, add setBounds(int, int, int, int) if (location != null && !setLocationFound && size != null && !setSizeFound) { widget.addMethodInvocation("setBounds(int,int,int,int)", location.x + ", " + location.y + ", " + size.width + ", " + size.height); return; } // something found, add as needed if (location != null && !setLocationFound) { widget.addMethodInvocation(SET_LOCATION_INT_INT, location.x + ", " + location.y); } if (size != null && !setSizeFound) { widget.addMethodInvocation(SET_SIZE_INT_INT, size.width + ", " + size.height); } } private boolean useCreationFlow() { return getToolkit().getPreferences().getBoolean(IPreferenceConstants.P_CREATION_FLOW); } private ToolkitDescription getToolkit() { return getDescription().getToolkit(); } /** * set the expression as integer value to avoid cast expressions in Designer code. */ private void setExpression(MethodInvocation mi, int index, int arg) throws Exception { getEditor().replaceExpression((Expression) mi.arguments().get(index), IntegerConverter.INSTANCE.toJavaSource(this, Integer.valueOf(arg))); } private void removeUnusedMethodsAfterSetBounds(ControlInfo widget) throws Exception { { MethodInvocation mi = widget.getMethodInvocation(SET_LOCATION_INT_INT); if (mi != null) { widget.removeMethodInvocations(SET_LOCATION_INT_INT); } } { MethodInvocation mi = widget.getMethodInvocation(SET_LOCATION_POINT); if (mi != null) { widget.removeMethodInvocations(SET_LOCATION_POINT); } } { MethodInvocation mi = widget.getMethodInvocation(SET_SIZE_INT_INT); if (mi != null) { widget.removeMethodInvocations(SET_SIZE_INT_INT); } } { MethodInvocation mi = widget.getMethodInvocation(SET_SIZE_POINT); if (mi != null) { widget.removeMethodInvocations(SET_SIZE_POINT); } } } //////////////////////////////////////////////////////////////////////////// // // Clipboard // //////////////////////////////////////////////////////////////////////////// @Override protected void clipboardCopy_addControlCommands(ControlInfo control, List<ClipboardCommand> commands) throws Exception { final Rectangle bounds = control.getModelBounds(); commands.add(new LayoutClipboardCommand<AbsoluteLayoutInfo>(control) { private static final long serialVersionUID = 0L; @Override protected void add(AbsoluteLayoutInfo layout, ControlInfo control) throws Exception { layout.command_CREATE(control, null); layout.commandChangeBounds(control, bounds.getLocation(), bounds.getSize()); } }); } //////////////////////////////////////////////////////////////////////////// // // Utils // //////////////////////////////////////////////////////////////////////////// /** * @return the {@link AbsoluteLayoutInfo} that generate code <code>setLayout(null)</code>. */ public static AbsoluteLayoutInfo createExplicit(CompositeInfo parent) throws Exception { AstEditor editor = parent.getEditor(); ToolkitDescription toolkit = parent.getDescription().getToolkit(); AbsoluteLayoutCreationSupport creationSupport = new AbsoluteLayoutCreationSupport(); return new AbsoluteLayoutInfo(editor, toolkit, creationSupport); } }