diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java index f7ba0fe868a4..0d0ccd7fa8d8 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java @@ -17,6 +17,7 @@ package com.intellij.android.designer.designSurface; import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.Result; +import com.android.ide.common.rendering.api.ViewInfo; import com.android.ide.common.resources.configuration.*; import com.android.resources.NightMode; import com.android.resources.UiMode; @@ -25,6 +26,7 @@ import com.intellij.android.designer.componentTree.AndroidTreeDecorator; import com.intellij.android.designer.model.RadViewComponent; import com.intellij.designer.componentTree.TreeComponentDecorator; import com.intellij.designer.designSurface.DesignerEditorPanel; +import com.intellij.designer.model.RadComponent; import com.intellij.openapi.module.Module; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.android.facet.AndroidFacet; @@ -41,6 +43,7 @@ import javax.xml.parsers.SAXParserFactory; import java.awt.*; import java.awt.image.BufferedImage; import java.io.InputStream; +import java.util.List; /** * @author Alexander Lobas @@ -52,6 +55,8 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel { public AndroidDesignerEditorPanel(@NotNull Module module, @NotNull VirtualFile file) { super(module, file); + // TODO: (profile|device|target|...|theme) panel + // TODO: use platform DOM // (temp code) @@ -116,10 +121,15 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel { } } + RootView rootView = new RootView(mySession.getImage(), 30, 20); + updateRootComponent(mySession.getRootViews(), rootView); + JPanel rootPanel = new JPanel(null); - rootPanel.add(new RootView(mySession.getImage(), 30, 20)); rootPanel.setBackground(Color.WHITE); + rootPanel.add(rootView); + myLayeredPane.add(rootPanel, LAYER_COMPONENT); + showDesignerCard(); } catch (Throwable e) { @@ -127,6 +137,46 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel { } } + private void updateRootComponent(List views, JComponent nativeComponent) { + RadViewComponent rootComponent = (RadViewComponent)myRootComponent; + + int size = views.size(); + if (size == 1) { + RadViewComponent newRootComponent = new RadViewComponent(null, "Device Screen"); + newRootComponent.getChildren().add(rootComponent); + rootComponent.setParent(newRootComponent); + + updateComponent(rootComponent, views.get(0), nativeComponent, 0, 0); + + myRootComponent = rootComponent = newRootComponent; + } + else { + List children = rootComponent.getChildren(); + for (int i = 0; i < size; i++) { + updateComponent((RadViewComponent)children.get(i), views.get(i), nativeComponent, 0, 0); + } + } + + rootComponent.setNativeComponent(nativeComponent); + rootComponent.setBounds(0, 0, nativeComponent.getWidth(), nativeComponent.getHeight()); + } + + private static void updateComponent(RadViewComponent component, ViewInfo view, JComponent nativeComponent, int parentX, int parentY) { + component.setNativeComponent(nativeComponent); + + int left = parentX + view.getLeft(); + int top = parentY + view.getTop(); + component.setBounds(left, top, view.getRight() - view.getLeft(), view.getBottom() - view.getTop()); + + List views = view.getChildren(); + List children = component.getChildren(); + int size = views.size(); + + for (int i = 0; i < size; i++) { + updateComponent((RadViewComponent)children.get(i), views.get(i), nativeComponent, left, top); + } + } + @Override public TreeComponentDecorator getTreeDecorator() { return myTreeDecorator; diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/RadViewComponent.java b/plugins/android-designer/src/com/intellij/android/designer/model/RadViewComponent.java index 0ece1b2c1695..e1395669ee78 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/RadViewComponent.java +++ b/plugins/android-designer/src/com/intellij/android/designer/model/RadViewComponent.java @@ -17,6 +17,7 @@ package com.intellij.android.designer.model; import com.intellij.designer.model.RadComponent; +import javax.swing.*; import java.awt.*; import java.util.ArrayList; import java.util.List; @@ -29,6 +30,8 @@ import java.util.List; public class RadViewComponent extends RadComponent { private final String myTitle; private final List myChildren = new ArrayList(); + private Component myNativeComponent; + private final Rectangle myBounds = new Rectangle(); public RadViewComponent(RadViewComponent parent, String title) { myTitle = title; @@ -49,6 +52,24 @@ public class RadViewComponent extends RadComponent { @Override public Rectangle getBounds() { - return new Rectangle(); + return myBounds; + } + + public void setBounds(int x, int y, int width, int height) { + myBounds.setBounds(x, y, width, height); + } + + public void setNativeComponent(Component nativeComponent) { + myNativeComponent = nativeComponent; + } + + @Override + public Point convertPoint(Component component, int x, int y) { + return SwingUtilities.convertPoint(component, x, y, myNativeComponent); + } + + @Override + public Point convertPoint(int x, int y, Component component) { + return SwingUtilities.convertPoint(myNativeComponent, x, y, component); } } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/ComponentTreeBuilder.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/ComponentTreeBuilder.java index 300ec7d98fe2..c2f2c0b0bea2 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/ComponentTreeBuilder.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/ComponentTreeBuilder.java @@ -15,22 +15,82 @@ */ package com.intellij.designer.componentTree; +import com.intellij.designer.designSurface.ComponentSelectionListener; import com.intellij.designer.designSurface.DesignerEditorPanel; +import com.intellij.designer.designSurface.EditableArea; +import com.intellij.designer.model.RadComponent; import com.intellij.ide.util.treeView.AbstractTreeBuilder; -import com.intellij.ide.util.treeView.AbstractTreeStructure; -import com.intellij.ide.util.treeView.NodeDescriptor; -import org.jetbrains.annotations.Nullable; -import javax.swing.*; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeModel; -import java.util.Comparator; +import javax.swing.tree.TreeSelectionModel; /** * @author Alexander Lobas */ -public final class ComponentTreeBuilder extends AbstractTreeBuilder { +public final class ComponentTreeBuilder extends AbstractTreeBuilder implements ComponentSelectionListener, TreeSelectionListener { + private final EditableArea mySurfaceArea; + private final TreeSelectionModel myTreeSelectionModel; + public ComponentTreeBuilder(ComponentTree tree, DesignerEditorPanel designer) { super(tree, (DefaultTreeModel)tree.getModel(), new TreeContentProvider(designer), null); // TODO: comparator? initRootNode(); + mySurfaceArea = designer.getSurfaceArea(); + myTreeSelectionModel = getTree().getSelectionModel(); + // TODO: restore expanded state + setTreeSelection(); + addListeners(); + } + + @Override + public void dispose() { + removeListeners(); + super.dispose(); + } + + private void addListeners() { + mySurfaceArea.addSelectionListener(this); + myTreeSelectionModel.addTreeSelectionListener(this); + } + + private void removeListeners() { + mySurfaceArea.removeSelectionListener(this); + myTreeSelectionModel.removeTreeSelectionListener(this); + } + + private void handleSelection(Runnable runnable) { + try { + removeListeners(); + runnable.run(); + } + finally { + addListeners(); + } + } + + @Override + public void selectionChanged(EditableArea area) { + handleSelection(new Runnable() { + @Override + public void run() { + queueUpdate(); + setTreeSelection(); + } + }); + } + + private void setTreeSelection() { + select(mySurfaceArea.getSelection().toArray(), null); + } + + @Override + public void valueChanged(TreeSelectionEvent e) { + handleSelection(new Runnable() { + @Override + public void run() { + mySurfaceArea.setSelection(getSelectedElements(RadComponent.class)); + } + }); } } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/CaptionPanel.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/CaptionPanel.java index cbd1efb569a3..248e0cb3396f 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/CaptionPanel.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/CaptionPanel.java @@ -26,7 +26,7 @@ import java.awt.*; /** * @author Alexander Lobas */ -public class CaptionPanel extends JComponent implements EditableArea, DataProvider { +public class CaptionPanel extends JComponent implements DataProvider { public CaptionPanel(DesignerEditorPanel designer, boolean horizontal) { setBorder(IdeBorderFactory.createBorder(horizontal ? SideBorder.BOTTOM : SideBorder.RIGHT)); diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/ComponentSelectionListener.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/ComponentSelectionListener.java new file mode 100644 index 000000000000..5f1d1c74504d --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/ComponentSelectionListener.java @@ -0,0 +1,25 @@ +/* + * 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.designer.designSurface; + +import java.util.EventListener; + +/** + * @author Alexander Lobas + */ +public interface ComponentSelectionListener extends EventListener { + void selectionChanged(EditableArea area); +} diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/DecorationLayer.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/DecorationLayer.java new file mode 100644 index 000000000000..da18beb5e39d --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/DecorationLayer.java @@ -0,0 +1,46 @@ +/* + * 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.designer.designSurface; + +import com.intellij.designer.model.RadComponent; + +import javax.swing.*; +import java.awt.*; + +/** + * @author Alexander Lobas + */ +public class DecorationLayer extends JComponent { + private final EditableArea myArea; + + public DecorationLayer(EditableArea area) { + myArea = area; + } + + @Override + public void paint(Graphics g) { + g.setColor(Color.RED); + ((Graphics2D)g).setStroke(new BasicStroke(2)); + + for (RadComponent component : myArea.getSelection()) { + Rectangle bounds = component.getBounds(); + Point location = component.convertPoint(bounds.x, bounds.y, this); + g.drawRect(location.x, location.y, bounds.width, bounds.height); + } + + paintChildren(g); + } +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/DesignerEditorPanel.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/DesignerEditorPanel.java index ce78ac1998d5..c03325a2b295 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/DesignerEditorPanel.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/DesignerEditorPanel.java @@ -18,7 +18,9 @@ package com.intellij.designer.designSurface; import com.intellij.designer.componentTree.TreeComponentDecorator; import com.intellij.designer.designSurface.tools.InputTool; import com.intellij.designer.designSurface.tools.SelectionTool; +import com.intellij.designer.designSurface.tools.ToolProvider; import com.intellij.designer.model.RadComponent; +import com.intellij.designer.model.RadComponentVisitor; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.module.Module; @@ -35,7 +37,7 @@ import java.awt.*; /** * @author Alexander Lobas */ -public abstract class DesignerEditorPanel extends JPanel implements DataProvider { +public abstract class DesignerEditorPanel extends JPanel implements ToolProvider, DataProvider { private final CardLayout myLayout = new CardLayout(); protected static final Integer LAYER_COMPONENT = JLayeredPane.DEFAULT_LAYER; @@ -53,7 +55,8 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider private JScrollPane myScrollPane; protected JLayeredPane myLayeredPane; private GlassLayer myGlassLayer; - private InputTool myTool = new SelectionTool(); + private InputTool myTool; + protected EditableArea mySurfaceArea; @NonNls private final static String ERROR_CARD = "error"; private JLabel myErrorLabel; @@ -64,6 +67,7 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider setLayout(myLayout); createDesignerCard(); createErrorCard(); + loadDefaultTool(); } private void createDesignerCard() { @@ -73,8 +77,6 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 1; - gbc.weightx = 0.0; - gbc.weighty = 0.0; gbc.fill = GridBagConstraints.BOTH; myVerticalCaption = new CaptionPanel(this, false); @@ -87,13 +89,42 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider myDesignerCard.add(myHorizontalCaption, gbc); myLayeredPane = new MyLayeredPane(); - myGlassLayer = new GlassLayer(this); + mySurfaceArea = new EditableArea() { + @Override + public void setCursor(Cursor cursor) { + myLayeredPane.setCursor(cursor); + } + + @Override + public JComponent getNativeComponent() { + return myLayeredPane; + } + + @Override + protected void fireSelectionChanged() { + super.fireSelectionChanged(); + myLayeredPane.revalidate(); + myLayeredPane.repaint(); + } + + @Override + public RadComponent findTarget(int x, int y) { + if (myRootComponent != null) { + FindComponentVisitor visitor = new FindComponentVisitor(x, y); + myRootComponent.accept(visitor, false); + return visitor.getResult(); + } + return null; + } + }; + myGlassLayer = new GlassLayer(this, mySurfaceArea); myLayeredPane.add(myGlassLayer, LAYER_GLASS); + myLayeredPane.add(new DecorationLayer(mySurfaceArea), LAYER_DECORATION); gbc.gridx = 1; gbc.gridy = 1; - gbc.weightx = 1.0; - gbc.weighty = 1.0; + gbc.weightx = 1; + gbc.weighty = 1; myScrollPane = ScrollPaneFactory.createScrollPane(myLayeredPane); myScrollPane.setBackground(Color.WHITE); @@ -109,6 +140,33 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider myLayout.show(this, DESIGNER_CARD); } + public EditableArea getSurfaceArea() { + return mySurfaceArea; + } + + public InputTool getActiveTool() { + return myTool; + } + + @Override + public void setActiveTool(InputTool tool) { + if (myTool != null) { + myTool.deactivate(); + } + + myTool = tool; + + if (myTool != null) { + myTool.setToolProvider(this); + myTool.activate(); + } + } + + @Override + public void loadDefaultTool() { + setActiveTool(new SelectionTool()); + } + public final void showError(@NonNls String message, Throwable e) { myRootComponent = null; myErrorLabel.setText(message + e.toString()); @@ -119,10 +177,6 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider } } - public InputTool getActiveTool() { - return myTool; - } - @Override public Object getData(@NonNls String dataId) { return null; //To change body of implemented methods use File | Settings | File Templates. @@ -133,7 +187,7 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider } public JComponent getPreferredFocusedComponent() { - return null; // TODO: Auto-generated method stub + return myDesignerCard.isVisible() ? myGlassLayer : myErrorLabel; } public Object[] getTreeRoots() { @@ -196,4 +250,34 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider return false; } } + + private class FindComponentVisitor implements RadComponentVisitor { + private RadComponent myResult; + private final int myX; + private final int myY; + + public FindComponentVisitor(int x, int y) { + myX = x; + myY = y; + } + + public RadComponent getResult() { + return myResult; + } + + @Override + public boolean visit(RadComponent component) { + return myResult == null; + } + + @Override + public void endVisit(RadComponent component) { + if (myResult == null) { + Point location = component.convertPoint(myLayeredPane, myX, myY); + if (component.getBounds().contains(location)) { + myResult = component; + } + } + } + } } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/EditableArea.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/EditableArea.java index 62a6baa12593..83bf074f19b3 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/EditableArea.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/EditableArea.java @@ -15,8 +15,74 @@ */ package com.intellij.designer.designSurface; +import com.intellij.designer.model.RadComponent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.EventListenerList; +import java.awt.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + /** * @author Alexander Lobas */ -public interface EditableArea { +public abstract class EditableArea { + private final EventListenerList myListenerList = new EventListenerList(); + private List mySelection = new ArrayList(); + + public void addSelectionListener(ComponentSelectionListener listener) { + myListenerList.add(ComponentSelectionListener.class, listener); + } + + public void removeSelectionListener(ComponentSelectionListener listener) { + myListenerList.remove(ComponentSelectionListener.class, listener); + } + + protected void fireSelectionChanged() { + for (ComponentSelectionListener listener : myListenerList.getListeners(ComponentSelectionListener.class)) { + listener.selectionChanged(this); + } + } + + @NotNull + public List getSelection() { + return mySelection; + } + + public boolean isSelected(@NotNull RadComponent component) { + return mySelection.contains(component); + } + + public void select(@NotNull RadComponent component) { + mySelection = new ArrayList(); + mySelection.add(component); + fireSelectionChanged(); + } + + public void deselect(@NotNull RadComponent component) { + mySelection.remove(component); + fireSelectionChanged(); + } + + public void appendSelection(@NotNull RadComponent component) { + mySelection.remove(component); + mySelection.add(component); + fireSelectionChanged(); + } + + public void setSelection(@NotNull Collection components) { + mySelection = new ArrayList(components); + fireSelectionChanged(); + } + + public abstract void setCursor(@Nullable Cursor cursor); + + @NotNull + public abstract JComponent getNativeComponent(); + + @Nullable + public abstract RadComponent findTarget(int x, int y); } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/GlassLayer.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/GlassLayer.java index 6ee2a7436440..90c2c6631122 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/GlassLayer.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/GlassLayer.java @@ -16,6 +16,7 @@ package com.intellij.designer.designSurface; import com.intellij.designer.designSurface.tools.InputTool; +import com.intellij.designer.designSurface.tools.ToolProvider; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.ui.popup.PopupOwner; import org.jetbrains.annotations.NonNls; @@ -28,28 +29,30 @@ import java.awt.event.MouseEvent; /** * @author Alexander Lobas */ -public final class GlassLayer extends JComponent implements PopupOwner, EditableArea, DataProvider { - private final DesignerEditorPanel myDesigner; +public final class GlassLayer extends JComponent implements PopupOwner, DataProvider { + private final ToolProvider myToolProvider; + private final EditableArea myArea; - public GlassLayer(DesignerEditorPanel designer) { - myDesigner = designer; + public GlassLayer(ToolProvider provider, EditableArea area) { + myToolProvider = provider; + myArea = area; enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); } @Override protected void processKeyEvent(KeyEvent event) { try { - InputTool tool = myDesigner.getActiveTool(); + InputTool tool = myToolProvider.getActiveTool(); switch (event.getID()) { case KeyEvent.KEY_PRESSED: - tool.keyPressed(event, this); + tool.keyPressed(event, myArea); break; case KeyEvent.KEY_TYPED: - tool.keyTyped(event, this); + tool.keyTyped(event, myArea); break; case KeyEvent.KEY_RELEASED: - tool.keyReleased(event, this); + tool.keyReleased(event, myArea); break; } } @@ -67,24 +70,24 @@ public final class GlassLayer extends JComponent implements PopupOwner, Editable requestFocusInWindow(); } try { - InputTool tool = myDesigner.getActiveTool(); + InputTool tool = myToolProvider.getActiveTool(); switch (event.getID()) { case MouseEvent.MOUSE_PRESSED: - tool.mouseDown(event, this); + tool.mouseDown(event, myArea); break; case MouseEvent.MOUSE_RELEASED: - tool.mouseUp(event, this); + tool.mouseUp(event, myArea); break; case MouseEvent.MOUSE_ENTERED: - tool.mouseEntered(event, this); + tool.mouseEntered(event, myArea); break; case MouseEvent.MOUSE_EXITED: - tool.mouseExited(event, this); + tool.mouseExited(event, myArea); break; case MouseEvent.MOUSE_CLICKED: if (event.getClickCount() == 2) { - tool.mouseDoubleClick(event, this); + tool.mouseDoubleClick(event, myArea); } break; } @@ -97,14 +100,14 @@ public final class GlassLayer extends JComponent implements PopupOwner, Editable @Override protected void processMouseMotionEvent(MouseEvent event) { try { - InputTool tool = myDesigner.getActiveTool(); + InputTool tool = myToolProvider.getActiveTool(); switch (event.getID()) { case MouseEvent.MOUSE_MOVED: - tool.mouseMove(event, this); + tool.mouseMove(event, myArea); break; case MouseEvent.MOUSE_DRAGGED: - tool.mouseDrag(event, this); + tool.mouseDrag(event, myArea); break; } } @@ -114,7 +117,7 @@ public final class GlassLayer extends JComponent implements PopupOwner, Editable } private void handleException(Throwable e) { - myDesigner.showError("Edit error: ", e); + myToolProvider.showError("Edit error: ", e); } @Override diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/AbstractCreationTool.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/AbstractCreationTool.java new file mode 100644 index 000000000000..e4993205062a --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/AbstractCreationTool.java @@ -0,0 +1,22 @@ +/* + * 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.designer.designSurface.tools; + +/** + * @author Alexander Lobas + */ +public abstract class AbstractCreationTool extends TargetingTool { +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/CreationTool.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/CreationTool.java new file mode 100644 index 000000000000..19e143c1a97f --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/CreationTool.java @@ -0,0 +1,22 @@ +/* + * 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.designer.designSurface.tools; + +/** + * @author Alexander Lobas + */ +public class CreationTool extends AbstractCreationTool { +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/DragTracker.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/DragTracker.java new file mode 100644 index 000000000000..7c0a3347ab68 --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/DragTracker.java @@ -0,0 +1,22 @@ +/* + * 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.designer.designSurface.tools; + +/** + * @author Alexander Lobas + */ +public class DragTracker extends TargetingTool { +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/InputTool.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/InputTool.java index 0e14dc2d6196..f33a7931587e 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/InputTool.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/InputTool.java @@ -1,7 +1,7 @@ /* * Copyright 2000-2012 JetBrains s.r.o. * - * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the Apache Licensevent, 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 * @@ -16,7 +16,11 @@ package com.intellij.designer.designSurface.tools; import com.intellij.designer.designSurface.EditableArea; +import org.jetbrains.annotations.Nullable; +import javax.swing.*; +import java.awt.*; +import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; @@ -24,60 +28,272 @@ import java.awt.event.MouseEvent; * @author Alexander Lobas */ public abstract class InputTool { - /** - * The final state for a tool to be in. Once a tool reaches this state, it will not change states - * until it is activated() again. - */ protected static final int STATE_NONE = 0; - /** - * The first state that a tool is in. The tool will generally be in this state immediately - * following {@link #activate()}. - */ protected static final int STATE_INIT = 1; - /** - * The state indicating that one or more buttons is pressed, but the user has not moved past the - * drag threshold. Many tools will do nothing during this state but wait until - * {@link #STATE_DRAG_IN_PROGRESS} is entered. - */ protected static final int STATE_DRAG = 2; - /** - * The state indicating that the drag detection theshold has been passed, and a drag is in - * progress. - */ protected static final int STATE_DRAG_IN_PROGRESS = 3; - /** - * The state indicating that an input event has invalidated the interaction. For example, during a - * mouse drag, pressing additional mouse button might invalidate the drag. - */ protected static final int STATE_INVALID = 4; - public void keyTyped(KeyEvent e, EditableArea area) throws Exception { + private ToolProvider myToolProvider; + protected EditableArea myArea; + private Object myCommand; + private boolean myActive; + private boolean myCanUnload = true; + protected int myState; + protected int myCurrentScreenX; + protected int myCurrentScreenY; + protected InputEvent myInputEvent; + protected int myButton; + protected int myStartScreenX; + protected int myStartScreenY; + private boolean myCanPastThreshold; + + ////////////////////////////////////////////////////////////////////////////////////////// + // + // State + // + ////////////////////////////////////////////////////////////////////////////////////////// + + public void activate() { + resetState(); + myState = STATE_INIT; + myActive = true; } - public void keyPressed(KeyEvent e, EditableArea area) throws Exception { + public void deactivate() { + myActive = false; + setCommand(null); } - public void keyReleased(KeyEvent e, EditableArea area) throws Exception { + protected void resetState() { + myCurrentScreenX = 0; + myCurrentScreenY = 0; + myInputEvent = null; + myButton = 0; + myStartScreenX = 0; + myStartScreenY = 0; + myCanPastThreshold = false; } - public void mouseDown(MouseEvent e, EditableArea area) throws Exception { + public final void setToolProvider(ToolProvider provider) { + myToolProvider = provider; } - public void mouseUp(MouseEvent e, EditableArea area) throws Exception { + public final void setArea(@Nullable EditableArea area) { + if (myArea != area) { + setCursor(null); + myArea = area; + + if (myArea != null) { + Point mouseLocation = MouseInfo.getPointerInfo().getLocation(); + SwingUtilities.convertPointFromScreen(mouseLocation, myArea.getNativeComponent()); + myCurrentScreenX = mouseLocation.x; + myCurrentScreenY = mouseLocation.y; + } + + refreshCursor(); + } } - public void mouseMove(MouseEvent e, EditableArea area) throws Exception { + protected final boolean unloadWhenFinished() { + return myCanUnload; } - public void mouseDrag(MouseEvent e, EditableArea area) throws Exception { + public final void setUnloadWhenFinished(boolean value) { + myCanUnload = value; } - public void mouseDoubleClick(MouseEvent e, EditableArea area) throws Exception { + protected final void setCommand(@Nullable Object command) { + myCommand = command; + refreshCursor(); } - public void mouseEntered(MouseEvent e, EditableArea area) throws Exception { + ////////////////////////////////////////////////////////////////////////////////////////// + // + // Cursor + // + ////////////////////////////////////////////////////////////////////////////////////////// + private Cursor myDefaultCursor; + private Cursor myDisabledCursor; + + protected final void setCursor(@Nullable Cursor cursor) { + if (myArea != null) { + myArea.setCursor(cursor); + } } - public void mouseExited(MouseEvent e, EditableArea area) throws Exception { + public void refreshCursor() { + if (myActive) { + setCursor(calculateCursor()); + } + } + + @Nullable + protected Cursor calculateCursor() { + if (myState == STATE_NONE) { + return null; + } + if (myCommand == null) { + return getDisabledCursor(); + } + return getDefaultCursor(); + } + + protected final Cursor getDefaultCursor() { + return myDefaultCursor; + } + + public final void setDefaultCursor(@Nullable Cursor cursor) { + if (myDefaultCursor != cursor) { + myDefaultCursor = cursor; + refreshCursor(); + } + } + + protected final Cursor getDisabledCursor() { + return myDisabledCursor == null ? getDefaultCursor() : myDisabledCursor; + } + + public final void setDisabledCursor(@Nullable Cursor cursor) { + if (myDisabledCursor != cursor) { + myDisabledCursor = cursor; + refreshCursor(); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // + // Keyboard + // + ////////////////////////////////////////////////////////////////////////////////////////// + + public void keyTyped(KeyEvent event, EditableArea area) throws Exception { + } + + public void keyPressed(KeyEvent event, EditableArea area) throws Exception { + } + + public void keyReleased(KeyEvent event, EditableArea area) throws Exception { + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // + // Mouse + // + ////////////////////////////////////////////////////////////////////////////////////////// + private static final int DRAG_THRESHOLD = 5; + + private void setEvent(MouseEvent event) { + myCurrentScreenX = event.getX(); + myCurrentScreenY = event.getY(); + myButton = event.getButton(); + myInputEvent = event; + } + + private boolean movedPastThreshold() { + if (!myCanPastThreshold) { + myCanPastThreshold = + Math.abs(myStartScreenX - myCurrentScreenX) > DRAG_THRESHOLD + || Math.abs(myStartScreenY - myCurrentScreenY) > DRAG_THRESHOLD; + } + return myCanPastThreshold; + } + + protected void handleButtonDown(int button) { + } + + protected void handleButtonUp(int button) { + } + + protected void handleMove() { + } + + protected void handleDrag() { + } + + protected void handleDragStarted() { + } + + protected void handleDragInProgress() { + } + + protected void handleDoubleClick(int button) { + } + + protected void handleAreaExited() { + } + + protected void handleFinished() { + if (myCanUnload) { + myToolProvider.loadDefaultTool(); + } + else { + deactivate(); + activate(); + } + } + + public void mouseDown(MouseEvent event, EditableArea area) throws Exception { + setArea(area); + setEvent(event); + myStartScreenX = event.getX(); + myStartScreenY = event.getY(); + handleButtonDown(event.getButton()); + } + + public void mouseUp(MouseEvent event, EditableArea area) throws Exception { + setArea(area); + setEvent(event); + handleButtonUp(event.getButton()); + } + + public void mouseMove(MouseEvent event, EditableArea area) throws Exception { + setArea(area); + setEvent(event); + + if (myState == STATE_DRAG_IN_PROGRESS) { + handleDragInProgress(); + } + else { + handleMove(); + } + } + + public void mouseDrag(MouseEvent event, EditableArea area) throws Exception { + setArea(area); + boolean wasDragging = movedPastThreshold(); + setEvent(event); + handleDrag(); + + if (movedPastThreshold()) { + if (!wasDragging) { + handleDragStarted(); + } + handleDragInProgress(); + } + } + + public void mouseDoubleClick(MouseEvent event, EditableArea area) throws Exception { + setArea(area); + setEvent(event); + handleDoubleClick(event.getButton()); + } + + public void mouseEntered(MouseEvent event, EditableArea area) throws Exception { + setEvent(event); + + if (myArea != null) { + handleAreaExited(); + } + + setArea(area); + } + + public void mouseExited(MouseEvent event, EditableArea area) throws Exception { + if (myArea == area) { + setEvent(event); + handleAreaExited(); + setArea(null); + } } } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/MarqueeTracker.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/MarqueeTracker.java new file mode 100644 index 000000000000..4d2e58ec89f8 --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/MarqueeTracker.java @@ -0,0 +1,22 @@ +/* + * 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.designer.designSurface.tools; + +/** + * @author Alexander Lobas + */ +public class MarqueeTracker extends InputTool { +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/PasteTool.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/PasteTool.java new file mode 100644 index 000000000000..8360cdf1a5ce --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/PasteTool.java @@ -0,0 +1,22 @@ +/* + * 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.designer.designSurface.tools; + +/** + * @author Alexander Lobas + */ +public class PasteTool extends AbstractCreationTool { +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/ResizeTracker.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/ResizeTracker.java new file mode 100644 index 000000000000..da9c21e022ae --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/ResizeTracker.java @@ -0,0 +1,22 @@ +/* + * 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.designer.designSurface.tools; + +/** + * @author Alexander Lobas + */ +public class ResizeTracker extends InputTool { +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/SelectionTool.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/SelectionTool.java index 8bbec2018fe4..b3a3921edccd 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/SelectionTool.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/SelectionTool.java @@ -15,8 +15,86 @@ */ package com.intellij.designer.designSurface.tools; +import com.intellij.designer.designSurface.EditableArea; +import com.intellij.designer.model.RadComponent; +import com.intellij.designer.utils.Cursors; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.util.List; + /** * @author Alexander Lobas */ -public class SelectionTool extends TargetingTool { +public class SelectionTool extends InputTool { + public SelectionTool() { + setDisabledCursor(Cursors.getNoCursor()); + } + + private void performSelection(RadComponent component) { + if (myInputEvent.isControlDown()) { + if (myArea.isSelected(component)) { + myArea.deselect(component); + } + else { + myArea.appendSelection(component); + } + } + else if (myInputEvent.isShiftDown()) { + myArea.appendSelection(component); + } + else { + myArea.select(component); + } + } + + @Override + protected void handleButtonDown(int button) { + if (myState == STATE_INIT && (button == 1 || button == 3)) { + RadComponent component = myArea.findTarget(myCurrentScreenX, myCurrentScreenY); + if (component != null) { + performSelection(component); + } + } + if (button == 1) { + if (myState == STATE_INIT) { + myState = STATE_DRAG; + } + } + else { + if (button == 3) { + myState = STATE_NONE; + } + else { + myState = STATE_INVALID; + } + setCommand(null); + } + } + + @Override + protected void handleButtonUp(int button) { + myState = STATE_INIT; + refreshCursor(); + } + + protected Cursor calculateCursor() { + return myState == STATE_INIT || myState == STATE_DRAG + ? getDefaultCursor() + : super.calculateCursor(); + } + + @Override + public void keyPressed(KeyEvent event, EditableArea area) throws Exception { + if (event.getKeyCode() == KeyEvent.VK_ESCAPE) { + List selection = area.getSelection(); + if (!selection.isEmpty()) { + RadComponent component = selection.get(0).getParent(); + if (component != null) { + area.select(component); + } + } + } + } } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/ToolProvider.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/ToolProvider.java new file mode 100644 index 000000000000..869cecacb87b --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/ToolProvider.java @@ -0,0 +1,31 @@ +/* + * 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.designer.designSurface.tools; + +import org.jetbrains.annotations.NonNls; + +/** + * @author Alexander Lobas + */ +public interface ToolProvider { + InputTool getActiveTool(); + + void setActiveTool(InputTool tool); + + void loadDefaultTool(); + + void showError(@NonNls String message, Throwable e); +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java index da8d9486963b..f2b307f7a45b 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java @@ -56,10 +56,6 @@ public abstract class RadComponent { return getChildren(); } - public JComponent getNativeRootComponent() { - return null; - } - public Rectangle getBounds() { return null; } @@ -91,4 +87,22 @@ public abstract class RadComponent { public final void putClientProperty(@NotNull Object key, Object value) { myClientProperties.put(key, value); } + + public void accept(RadComponentVisitor visitor, boolean forward) { + if (visitor.visit(this)) { + List children = getChildren(); + if (forward) { + for (RadComponent child : children) { + child.accept(visitor, forward); + } + } + else { + int size = children.size(); + for (int i = size - 1; i >= 0; i--) { + children.get(i).accept(visitor, forward); + } + } + visitor.endVisit(this); + } + } } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponentVisitor.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponentVisitor.java new file mode 100644 index 000000000000..11f06579e5ce --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponentVisitor.java @@ -0,0 +1,25 @@ +/* + * 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.designer.model; + +/** + * @author Alexander Lobas + */ +public interface RadComponentVisitor { + boolean visit(RadComponent component); + + void endVisit(RadComponent component); +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/utils/Cursors.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/utils/Cursors.java new file mode 100644 index 000000000000..858f65f02c52 --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/utils/Cursors.java @@ -0,0 +1,32 @@ +/* + * 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.designer.utils; + +import java.awt.*; + +/** + * @author Alexander Lobas + */ +public final class Cursors { + public static Cursor getNoCursor() { + try { + return Cursor.getSystemCustomCursor("MoveNoDrop.32x32"); + } + catch (Exception ex) { + return Cursor.getDefaultCursor(); + } + } +} \ No newline at end of file