| /* |
| * 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; |
| |
| import com.android.SdkConstants; |
| import com.intellij.android.designer.designSurface.AbstractEditOperation; |
| import com.intellij.android.designer.designSurface.feedbacks.TextFeedback; |
| import com.intellij.android.designer.designSurface.graphics.DesignerGraphics; |
| import com.intellij.android.designer.designSurface.graphics.DrawingStyle; |
| import com.intellij.android.designer.model.RadViewComponent; |
| import com.intellij.android.designer.model.layout.Gravity; |
| import com.intellij.designer.designSurface.FeedbackLayer; |
| import com.intellij.designer.designSurface.OperationContext; |
| import com.intellij.designer.model.RadComponent; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.ui.IdeBorderFactory; |
| import com.intellij.util.containers.hash.HashSet; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| import java.util.Collections; |
| import java.util.Set; |
| |
| /** |
| * @author Alexander Lobas |
| */ |
| public class FrameLayoutOperation extends AbstractEditOperation { |
| private Set<Pair<Gravity, Gravity>> myExcludes = Collections.emptySet(); |
| private GravityFeedback myFeedback; |
| private TextFeedback myTextFeedback; |
| private Rectangle myBounds; |
| private String myGravity; |
| |
| public FrameLayoutOperation(RadComponent container, OperationContext context) { |
| super(container, context); |
| |
| if (context.isMove()) { |
| myExcludes = new HashSet<Pair<Gravity, Gravity>>(); |
| |
| for (RadComponent component : context.getComponents()) { |
| myExcludes.add(Gravity.getSides(component)); |
| } |
| } |
| } |
| |
| private void createFeedback() { |
| if (myFeedback == null) { |
| FeedbackLayer layer = myContext.getArea().getFeedbackLayer(); |
| myBounds = ((RadViewComponent)myContainer).getPaddedBounds(layer); |
| |
| myFeedback = new GravityFeedback(); |
| myFeedback.setBounds(myBounds); |
| layer.add(myFeedback); |
| |
| myTextFeedback = new TextFeedback(); |
| myTextFeedback.setBorder(IdeBorderFactory.createEmptyBorder(0, 3, 2, 0)); |
| layer.add(myTextFeedback); |
| |
| layer.repaint(); |
| } |
| } |
| |
| @Override |
| public void showFeedback() { |
| createFeedback(); |
| |
| Point location = myContext.getLocation(); |
| Gravity horizontal = calculateHorizontal(myBounds, location); |
| Gravity vertical = calculateVertical(myBounds, location); |
| |
| if (myContext.isMove() && exclude(horizontal, vertical)) { |
| horizontal = vertical = null; |
| } |
| |
| myFeedback.setGravity(horizontal, vertical); |
| configureTextFeedback(myBounds, horizontal, vertical); |
| |
| myGravity = Gravity.getValue(horizontal, vertical); |
| } |
| |
| private void configureTextFeedback(Rectangle bounds, @Nullable Gravity horizontal, @Nullable Gravity vertical) { |
| myTextFeedback.clear(); |
| |
| if (horizontal == null || vertical == null) { |
| myTextFeedback.bold("None"); |
| } |
| else { |
| myTextFeedback.bold("[" + vertical.name() + ", " + horizontal.name() + "]"); |
| } |
| |
| myTextFeedback.centerTop(bounds); |
| } |
| |
| @Override |
| public void eraseFeedback() { |
| if (myFeedback != null) { |
| FeedbackLayer layer = myContext.getArea().getFeedbackLayer(); |
| layer.remove(myFeedback); |
| layer.remove(myTextFeedback); |
| layer.repaint(); |
| myFeedback = null; |
| myTextFeedback = null; |
| } |
| } |
| |
| @Nullable |
| private static Gravity calculateHorizontal(Rectangle bounds, Point location) { |
| if (bounds.width < 10) { |
| return Gravity.left; |
| } |
| |
| Gravity horizontal = null; |
| double left = bounds.x + bounds.width / 3.0; |
| double right = bounds.x + 2 * bounds.width / 3.0; |
| |
| if (location.x < left) { |
| horizontal = Gravity.left; |
| } |
| else if (left < location.x && location.x < right) { |
| horizontal = Gravity.center; |
| } |
| else if (location.x > right) { |
| horizontal = Gravity.right; |
| } |
| |
| return horizontal; |
| } |
| |
| @Nullable |
| private static Gravity calculateVertical(Rectangle bounds, Point location) { |
| if (bounds.height < 10) { |
| return Gravity.top; |
| } |
| |
| Gravity vertical = null; |
| double top = bounds.y + bounds.height / 3.0; |
| double bottom = bounds.y + 2 * bounds.height / 3.0; |
| |
| if (location.y < top) { |
| vertical = Gravity.top; |
| } |
| else if (top < location.y && location.y < bottom) { |
| vertical = Gravity.center; |
| } |
| else if (location.y > bottom) { |
| vertical = Gravity.bottom; |
| } |
| |
| return vertical; |
| } |
| |
| private boolean exclude(@Nullable Gravity horizontal, @Nullable Gravity vertical) { |
| for (Pair<Gravity, Gravity> p : myExcludes) { |
| if (p.first == horizontal && p.second == vertical) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public boolean canExecute() { |
| return myGravity != null; |
| } |
| |
| @Override |
| public void execute() throws Exception { |
| super.execute(); |
| |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| @Override |
| public void run() { |
| for (RadComponent component : myComponents) { |
| ((RadViewComponent)component).getTag().setAttribute("layout_gravity", SdkConstants.NS_RESOURCES, myGravity); |
| } |
| } |
| }); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Feedback |
| // |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| private static final Gravity[] HORIZONTALS = {Gravity.left, Gravity.center, Gravity.right}; |
| private static final Gravity[] VERTICALS = {Gravity.top, Gravity.center, Gravity.bottom}; |
| |
| private class GravityFeedback extends JComponent { |
| @Nullable private Gravity myHorizontal; |
| @Nullable private Gravity myVertical; |
| |
| public GravityFeedback() { |
| } |
| |
| public void setGravity(@Nullable Gravity horizontal, @Nullable Gravity vertical) { |
| myHorizontal = horizontal; |
| myVertical = vertical; |
| repaint(); |
| } |
| |
| @Override |
| protected void paintComponent(Graphics g) { |
| super.paintComponent(g); |
| |
| Rectangle bounds = ((RadViewComponent)myContainer).getPaddedBounds(this); |
| DesignerGraphics.drawRect(DrawingStyle.DROP_PREVIEW, g, bounds.x, bounds.y, bounds.width, bounds.height); |
| |
| for (Gravity h : HORIZONTALS) { |
| for (Gravity v : VERTICALS) { |
| boolean selection = (myHorizontal == h && myVertical == v); |
| if (selection || !exclude(h, v)) { |
| paintCell(g, h, v, selection); |
| } |
| } |
| } |
| } |
| |
| private boolean paintCell(Graphics g, Gravity horizontal, Gravity vertical, boolean selection) { |
| int x = 0; |
| int width = (getWidth() - 2) / 3; |
| if (horizontal == Gravity.center) { |
| x = width + 1; |
| } |
| else if (horizontal == Gravity.right) { |
| x = getWidth() - width; |
| } |
| |
| int y = 0; |
| int height = (getHeight() - 2) / 3; |
| if (vertical == Gravity.center) { |
| y = height + 1; |
| } |
| else if (vertical == Gravity.bottom) { |
| y = getHeight() - height; |
| } |
| |
| int hSpace = Math.min(5, Math.max(1, getWidth() / 30)); |
| if (hSpace > 1) { |
| x += hSpace; |
| width -= 2 * hSpace; |
| } |
| |
| int vSpace = Math.min(5, Math.max(1, getHeight() / 30)); |
| if (vSpace > 1) { |
| y += vSpace; |
| height -= 2 * vSpace; |
| } |
| |
| if (selection) { |
| if (myHorizontal == horizontal && myVertical == vertical) { |
| DesignerGraphics.drawFilledRect(DrawingStyle.DROP_ZONE_ACTIVE, g, x, y, width, height); |
| |
| return true; |
| } |
| } |
| else { |
| DesignerGraphics.drawFilledRect(DrawingStyle.DROP_ZONE, g, x, y, width, height); |
| } |
| |
| return false; |
| } |
| } |
| } |