blob: 4e51ab5dc858374605c350fa0e9c460af8fc614f [file] [log] [blame]
/*
* 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();
}
}