/* * Copyright 2000-2012 JetBrains s.r.o. * * 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 com.intellij.android.designer.designSurface.layout.actions; import com.android.SdkConstants; import com.android.tools.idea.designer.Insets; import com.intellij.android.designer.AndroidDesignerUtils; import com.intellij.android.designer.designSurface.feedbacks.TextFeedback; import com.intellij.android.designer.designSurface.graphics.*; import com.intellij.android.designer.model.RadComponentOperations; import com.intellij.android.designer.model.RadViewComponent; import com.intellij.designer.designSurface.*; import com.intellij.designer.designSurface.feedbacks.LineMarginBorder; import com.intellij.designer.designSurface.selection.EmptyPoint; import com.intellij.designer.model.RadComponent; import com.intellij.designer.utils.Position; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlTag; import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.List; import java.util.Locale; import static com.android.SdkConstants.VALUE_N_DP; /** * @author Alexander Lobas */ public class LayoutMarginOperation implements EditOperation { public static final String TYPE = "layout_margin"; protected final OperationContext myContext; protected RadViewComponent myComponent; protected RectangleFeedback myFeedback; protected TextFeedback myTextFeedback; private Rectangle myBounds; // in screen coordinates protected Insets myMargins; // in model coordinates public LayoutMarginOperation(OperationContext context) { myContext = context; } @Override public void setComponent(RadComponent component) { myComponent = (RadViewComponent)component; myBounds = myComponent.getBounds(myContext.getArea().getFeedbackLayer()); myMargins = myComponent.getMargins(); } @Override public void setComponents(List<RadComponent> components) { } private void createFeedback() { if (myFeedback == null) { FeedbackLayer layer = myContext.getArea().getFeedbackLayer(); myTextFeedback = new TextFeedback(); myTextFeedback.setBorder(new LineMarginBorder(0, 5, 3, 0)); layer.add(myTextFeedback); myFeedback = new RectangleFeedback(DrawingStyle.MARGIN_BOUNDS); layer.add(myFeedback); layer.repaint(); } } @Override public void showFeedback() { createFeedback(); Rectangle bounds = myContext.getTransformedRectangle(myBounds); FeedbackLayer layer = myContext.getArea().getFeedbackLayer(); myComponent.getMargins(layer).subtractFrom(bounds); myFeedback.setBounds(bounds); myTextFeedback.clear(); fillTextFeedback(); myTextFeedback.locationTo(myContext.getLocation(), 15); } protected void fillTextFeedback() { EditableArea area = myContext.getArea(); FeedbackLayer layer = area.getFeedbackLayer(); Dimension moveDelta = myComponent.toModel(layer, new Dimension(myContext.getMoveDelta().x, myContext.getMoveDelta().y)); Dimension sizeDelta = myComponent.toModel(layer, myContext.getSizeDelta()); int direction = myContext.getResizeDirection(); if (direction == Position.WEST) { // left myTextFeedback.append(AndroidDesignerUtils.pxToDpWithUnits(area, myMargins.left - moveDelta.width)); } else if (direction == Position.EAST) { // right myTextFeedback.append(AndroidDesignerUtils.pxToDpWithUnits(area, myMargins.right + sizeDelta.width)); } else if (direction == Position.NORTH) { // top myTextFeedback.append(AndroidDesignerUtils.pxToDpWithUnits(area, myMargins.top - moveDelta.height)); } else if (direction == Position.SOUTH) { // bottom myTextFeedback.append(AndroidDesignerUtils.pxToDpWithUnits(area, myMargins.bottom + sizeDelta.height)); } } @Override public void eraseFeedback() { if (myFeedback != null) { FeedbackLayer layer = myContext.getArea().getFeedbackLayer(); layer.remove(myTextFeedback); layer.remove(myFeedback); layer.repaint(); myTextFeedback = null; myFeedback = null; } } @Override public boolean canExecute() { return true; } @Override public void execute() throws Exception { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { XmlTag tag = myComponent.getTag(); XmlAttribute margin = tag.getAttribute("layout_margin", SdkConstants.NS_RESOURCES); if (margin != null) { String value = margin.getValue(); margin.delete(); if (!StringUtil.isEmpty(value)) { tag.setAttribute("layout_marginLeft", SdkConstants.NS_RESOURCES, value); tag.setAttribute("layout_marginRight", SdkConstants.NS_RESOURCES, value); tag.setAttribute("layout_marginTop", SdkConstants.NS_RESOURCES, value); tag.setAttribute("layout_marginBottom", SdkConstants.NS_RESOURCES, value); } } FeedbackLayer layer = myContext.getArea().getFeedbackLayer(); Dimension moveDelta = myComponent.toModel(layer, new Dimension(myContext.getMoveDelta().x, myContext.getMoveDelta().y)); Dimension sizeDelta = myComponent.toModel(layer, myContext.getSizeDelta()); int direction = myContext.getResizeDirection(); if (direction == Position.WEST) { // left setValue(tag, "layout_marginLeft", myMargins.left - moveDelta.width); } else if (direction == Position.EAST) { // right setValue(tag, "layout_marginRight", myMargins.right + sizeDelta.width); } else if (direction == Position.NORTH) { // top setValue(tag, "layout_marginTop", myMargins.top - moveDelta.height); } else if (direction == Position.SOUTH) { // bottom setValue(tag, "layout_marginBottom", myMargins.bottom + sizeDelta.height); } } }); } private void setValue(XmlTag tag, String name, int pxValue) { int value = AndroidDesignerUtils.pxToDp(myContext.getArea(), pxValue); if (value == 0) { RadComponentOperations.deleteAttribute(tag, name); } else { tag.setAttribute(name, SdkConstants.NS_RESOURCES, String.format(Locale.US, VALUE_N_DP, value)); } } ////////////////////////////////////////////////////////////////////////////////////////// // // ResizePoint // ////////////////////////////////////////////////////////////////////////////////////////// public static void points(ResizeSelectionDecorator decorator) { pointFeedback(decorator); decorator.addPoint(new DirectionResizePoint(DrawingStyle.MARGIN_HANDLE, Position.WEST, TYPE, "Change layout:margin.left") { // left @Override protected Point getLocation(DecorationLayer layer, RadComponent component) { Point location = super.getLocation(layer, component); int marginLeft = ((RadViewComponent)component).getMargins(layer).left; location.x -= marginLeft; return location; } }); pointRight(decorator, DrawingStyle.MARGIN_HANDLE, 0.25, TYPE, "Change layout:margin.right"); decorator.addPoint(new DirectionResizePoint(DrawingStyle.MARGIN_HANDLE, Position.NORTH, TYPE, "Change layout:margin.top") { // top @Override protected Point getLocation(DecorationLayer layer, RadComponent component) { Point location = super.getLocation(layer, component); int marginTop = ((RadViewComponent)component).getMargins(layer).top; location.y -= marginTop; return location; } }); pointBottom(decorator, DrawingStyle.MARGIN_HANDLE, 0.25, TYPE, "Change layout:margin.bottom"); } protected static void pointFeedback(ResizeSelectionDecorator decorator) { decorator.addPoint(new EmptyPoint() { @Override protected void paint(DecorationLayer layer, Graphics2D g, RadComponent component) { Rectangle bounds = component.getBounds(layer); Insets margins = ((RadViewComponent)component).getMargins(layer); if (!margins.isEmpty()) { margins.subtractFrom(bounds); DesignerGraphics.drawRect(DrawingStyle.MARGIN_BOUNDS, g, bounds.x, bounds.y, bounds.width, bounds.height); } } }); } protected static void pointRight(ResizeSelectionDecorator decorator, DrawingStyle style, double ySeparator, Object type, @Nullable String description) { decorator.addPoint(new DirectionResizePoint(style, Position.EAST, type, description) { @Override protected Point getLocation(DecorationLayer layer, RadComponent component) { Point location = super.getLocation(layer, component); int marginRight = ((RadViewComponent)component).getMargins(layer).right; location.x += marginRight; return location; } }.move(1, ySeparator)); } protected static void pointBottom(ResizeSelectionDecorator decorator, DrawingStyle style, double xSeparator, Object type, @Nullable String description) { decorator.addPoint(new DirectionResizePoint(style, Position.SOUTH, type, description) { @Override protected Point getLocation(DecorationLayer layer, RadComponent component) { Point location = super.getLocation(layer, component); int marginBottom = ((RadViewComponent)component).getMargins(layer).bottom; location.y += marginBottom; return location; } }.move(xSeparator, 1)); } }