| /* |
| * 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.componentTree; |
| |
| import com.intellij.designer.actions.DesignerActionPanel; |
| import com.intellij.designer.designSurface.*; |
| import com.intellij.designer.designSurface.tools.InputTool; |
| import com.intellij.designer.model.RadComponent; |
| import com.intellij.ide.util.treeView.AbstractTreeBuilder; |
| import com.intellij.openapi.actionSystem.ActionGroup; |
| import com.intellij.openapi.actionSystem.ActionPlaces; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.util.ArrayUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.event.EventListenerList; |
| import javax.swing.event.TreeSelectionEvent; |
| import javax.swing.event.TreeSelectionListener; |
| import javax.swing.tree.DefaultMutableTreeNode; |
| import javax.swing.tree.TreePath; |
| import java.awt.*; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| /** |
| * @author Alexander Lobas |
| */ |
| public final class TreeEditableArea implements EditableArea, FeedbackTreeLayer, TreeSelectionListener { |
| private final EventListenerList myListenerList = new EventListenerList(); |
| private final ComponentTree myTree; |
| private final AbstractTreeBuilder myTreeBuilder; |
| private final DesignerActionPanel myActionPanel; |
| private boolean myCanvasSelection; |
| |
| public TreeEditableArea(ComponentTree tree, AbstractTreeBuilder treeBuilder, DesignerActionPanel actionPanel) { |
| myTree = tree; |
| myTreeBuilder = treeBuilder; |
| myActionPanel = actionPanel; |
| hookSelection(); |
| } |
| |
| private void hookSelection() { |
| myTree.getSelectionModel().addTreeSelectionListener(this); |
| } |
| |
| public void unhookSelection() { |
| myTree.getSelectionModel().removeTreeSelectionListener(this); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Selection |
| // |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| @Override |
| public void addSelectionListener(ComponentSelectionListener listener) { |
| myListenerList.add(ComponentSelectionListener.class, listener); |
| } |
| |
| @Override |
| public void removeSelectionListener(ComponentSelectionListener listener) { |
| myListenerList.remove(ComponentSelectionListener.class, listener); |
| } |
| |
| private void fireSelectionChanged() { |
| for (ComponentSelectionListener listener : myListenerList.getListeners(ComponentSelectionListener.class)) { |
| listener.selectionChanged(this); |
| } |
| } |
| |
| @Override |
| public void valueChanged(TreeSelectionEvent e) { |
| if (!myTreeBuilder.isSelectionBeingAdjusted() && ApplicationManager.getApplication().isDispatchThread()) { |
| fireSelectionChanged(); |
| } |
| } |
| |
| @NotNull |
| @Override |
| public List<RadComponent> getSelection() { |
| return new ArrayList<RadComponent>(getRawSelection()); |
| } |
| |
| @Override |
| public boolean isSelected(@NotNull RadComponent component) { |
| return getRawSelection().contains(component); |
| } |
| |
| @Override |
| public void select(@NotNull RadComponent component) { |
| setRawSelection(component); |
| } |
| |
| @Override |
| public void deselect(@NotNull RadComponent component) { |
| Collection<RadComponent> selection = getRawSelection(); |
| selection.remove(component); |
| setRawSelection(selection); |
| } |
| |
| @Override |
| public void appendSelection(@NotNull RadComponent component) { |
| Collection<RadComponent> selection = getRawSelection(); |
| selection.add(component); |
| setRawSelection(selection); |
| } |
| |
| @Override |
| public void setSelection(@NotNull List<RadComponent> components) { |
| setRawSelection(components); |
| } |
| |
| @Override |
| public void deselect(@NotNull Collection<RadComponent> components) { |
| Collection<RadComponent> selection = getRawSelection(); |
| selection.removeAll(components); |
| setRawSelection(selection); |
| } |
| |
| @Override |
| public void deselectAll() { |
| setRawSelection(null); |
| } |
| |
| @Override |
| public void scrollToSelection() { |
| } |
| |
| private Collection<RadComponent> getRawSelection() { |
| return myTreeBuilder.getSelectedElements(RadComponent.class); |
| } |
| |
| private void setRawSelection(@Nullable Object value) { |
| unhookSelection(); |
| myTreeBuilder.queueUpdate(); |
| |
| if (value == null) { |
| myTreeBuilder.select(ArrayUtil.EMPTY_OBJECT_ARRAY, null); |
| myTree.clearSelection(); |
| } |
| else if (value instanceof RadComponent) { |
| myTreeBuilder.select(value); |
| } |
| else { |
| Collection collection = (Collection)value; |
| myTreeBuilder.select(collection.toArray(), null); |
| if (collection.isEmpty()) { |
| myTree.clearSelection(); |
| } |
| } |
| |
| myTreeBuilder.queueUpdate(); |
| hookSelection(); |
| fireSelectionChanged(); |
| } |
| |
| public boolean isCanvasSelection() { |
| return myCanvasSelection; |
| } |
| |
| public void setCanvasSelection(boolean canvasSelection) { |
| myCanvasSelection = canvasSelection; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Visual |
| // |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| @Override |
| public void setCursor(@Nullable Cursor cursor) { |
| myTree.setCursor(cursor); |
| } |
| |
| @Override |
| public void setDescription(@Nullable String text) { |
| } |
| |
| @NotNull |
| @Override |
| public JComponent getNativeComponent() { |
| return myTree; |
| } |
| |
| @Override |
| public RadComponent findTarget(int x, int y, @Nullable ComponentTargetFilter filter) { |
| TreePath path = myTree.getPathForLocation(x, y); |
| if (path != null) { |
| RadComponent component = myTree.extractComponent(path.getLastPathComponent()); |
| if (filter != null) { |
| while (component != null) { |
| if (filter.preFilter(component) && filter.resultFilter(component)) { |
| break; |
| } |
| component = component.getParent(); |
| } |
| } |
| return component; |
| } |
| return null; |
| } |
| |
| @Override |
| public InputTool findTargetTool(int x, int y) { |
| return null; |
| } |
| |
| @Override |
| public void showSelection(boolean value) { |
| } |
| |
| @Override |
| public ComponentDecorator getRootSelectionDecorator() { |
| return null; |
| } |
| |
| @Override |
| public EditOperation processRootOperation(OperationContext context) { |
| return null; |
| } |
| |
| @Override |
| public FeedbackLayer getFeedbackLayer() { |
| return null; |
| } |
| |
| @Override |
| public RadComponent getRootComponent() { |
| return null; |
| } |
| |
| @Override |
| public boolean isTree() { |
| return true; |
| } |
| |
| @Override |
| public FeedbackTreeLayer getFeedbackTreeLayer() { |
| return this; |
| } |
| |
| @Override |
| public ActionGroup getPopupActions() { |
| return myActionPanel.getPopupActions(this); |
| } |
| |
| @Override |
| public String getPopupPlace() { |
| return ActionPlaces.GUI_DESIGNER_COMPONENT_TREE_POPUP; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // FeedbackTreeLayer |
| // |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| private TreePath getPath(RadComponent component) { |
| // TODO: I don't know better way gets tree path for element |
| DefaultMutableTreeNode node = myTreeBuilder.getNodeForElement(component); |
| return node == null ? null : new TreePath(node.getPath()); |
| } |
| |
| @Override |
| public void mark(RadComponent component, int feedback) { |
| if (component != null) { |
| TreePath path = getPath(component); |
| if (feedback == INSERT_SELECTION) { |
| myTree.scrollPathToVisible(path); |
| if (!myTree.isExpanded(path)) { |
| myTreeBuilder.expand(component, null); |
| } |
| } |
| else { |
| myTree.scrollRowToVisible(myTree.getRowForPath(path) + (feedback == INSERT_BEFORE ? -1 : 1)); |
| } |
| } |
| myTree.mark(component, feedback); |
| } |
| |
| @Override |
| public void unmark() { |
| myTree.mark(null, -1); |
| } |
| |
| @Override |
| public boolean isBeforeLocation(RadComponent component, int x, int y) { |
| Rectangle bounds = myTree.getPathBounds(getPath(component)); |
| return bounds != null && y - bounds.y < myTree.getEdgeSize(); |
| } |
| |
| @Override |
| public boolean isAfterLocation(RadComponent component, int x, int y) { |
| Rectangle bounds = myTree.getPathBounds(getPath(component)); |
| return bounds != null && bounds.getMaxY() - y < myTree.getEdgeSize(); |
| } |
| } |