2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.uiDesigner
.componentTree
;
18 import com
.intellij
.ide
.util
.treeView
.AbstractTreeBuilder
;
19 import com
.intellij
.ide
.util
.treeView
.NodeDescriptor
;
20 import com
.intellij
.openapi
.diagnostic
.Logger
;
21 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
22 import com
.intellij
.openapi
.progress
.util
.StatusBarProgress
;
23 import com
.intellij
.openapi
.util
.Comparing
;
24 import com
.intellij
.uiDesigner
.FormEditingUtil
;
25 import com
.intellij
.uiDesigner
.HierarchyChangeListener
;
26 import com
.intellij
.uiDesigner
.SelectionWatcher
;
27 import com
.intellij
.uiDesigner
.designSurface
.GuiEditor
;
28 import com
.intellij
.uiDesigner
.propertyInspector
.PropertyInspector
;
29 import com
.intellij
.uiDesigner
.propertyInspector
.UIDesignerToolWindowManager
;
30 import com
.intellij
.uiDesigner
.radComponents
.RadComponent
;
31 import com
.intellij
.uiDesigner
.radComponents
.RadContainer
;
32 import org
.jetbrains
.annotations
.NotNull
;
34 import javax
.swing
.event
.TreeSelectionEvent
;
35 import javax
.swing
.event
.TreeSelectionListener
;
36 import javax
.swing
.tree
.DefaultTreeModel
;
37 import java
.util
.ArrayList
;
38 import java
.util
.Comparator
;
42 * @author Anton Katilin
43 * @author Vladimir Kondratyev
45 public final class ComponentTreeBuilder
extends AbstractTreeBuilder
{
46 private static final Logger LOG
= Logger
.getInstance("#com.intellij.componentTree.ComponentTreeBuilder");
48 private final GuiEditor myEditor
;
49 private final MySelectionWatcher mySelectionWatcher
;
51 * More then 0 if we are inside some change. In this case we have not
52 * react on our own events.
54 private int myInsideChange
;
55 private final MyHierarchyChangeListener myHierarchyChangeListener
;
56 private MyTreeSelectionListener myTreeSelectionListener
;
58 public ComponentTreeBuilder(final ComponentTree tree
, @NotNull final GuiEditor editor
) {
59 super(tree
,(DefaultTreeModel
)tree
.getModel(), new ComponentTreeStructure(editor
), MyComparator
.ourComparator
);
62 mySelectionWatcher
= new MySelectionWatcher(editor
);
67 myTreeSelectionListener
= new MyTreeSelectionListener();
68 myHierarchyChangeListener
= new MyHierarchyChangeListener();
69 getTree().getSelectionModel().addTreeSelectionListener(myTreeSelectionListener
);
70 editor
.addHierarchyChangeListener(myHierarchyChangeListener
);
74 public void dispose() {
75 myEditor
.removeHierarchyChangeListener(myHierarchyChangeListener
);
76 if (myTreeSelectionListener
!= null) {
77 getTree().getSelectionModel().removeTreeSelectionListener(myTreeSelectionListener
);
78 myTreeSelectionListener
= null;
80 mySelectionWatcher
.dispose();
84 private ComponentTreeStructure
getComponentTreeStructure(){
85 return (ComponentTreeStructure
)getTreeStructure();
88 protected boolean isAlwaysShowPlus(final NodeDescriptor descriptor
){
92 protected boolean isAutoExpandNode(final NodeDescriptor descriptor
){
93 return getComponentTreeStructure().isAutoExpandNode(descriptor
);
96 public void beginUpdateSelection() {
100 public void endUpdateSelection() {
106 * This method synchronizes selection in the tree with the selected
107 * RadComponent in the component hierarchy
109 private void syncSelection() {
110 // Found selected components
111 final RadContainer rootContainer
=myEditor
.getRootContainer();
112 final ArrayList
<RadComponent
> selection
= new ArrayList
<RadComponent
>();
113 FormEditingUtil
.iterate(
115 new FormEditingUtil
.ComponentVisitor
<RadComponent
>() {
116 public boolean visit(final RadComponent component
) {
117 if(component
.isSelected()){
118 selection
.add(component
);
124 if(selection
.size() == 0){
125 // If there is no selected component in the hierarchy, then
126 // we have to select RadRootContainer
127 selection
.add(rootContainer
);
130 final ComponentPtr
[] componentPtrs
= new ComponentPtr
[selection
.size()];
131 for (int i
= 0; i
< selection
.size(); i
++) {
132 componentPtrs
[i
] = new ComponentPtr(myEditor
, selection
.get(i
));
135 // Set selection in the tree
136 select(componentPtrs
, null);
138 // Notify the ComponentTree that selected component changed
139 myEditor
.fireSelectedComponentChanged();
143 protected ProgressIndicator
createProgressIndicator() {
144 return new StatusBarProgress();
148 * Compares RadComponent based on their natural order in the container.
150 private static final class MyComparator
implements Comparator
<NodeDescriptor
>{
151 public static final MyComparator ourComparator
=new MyComparator();
153 private static int indexOf(final RadContainer container
, final RadComponent component
){
154 if (container
!= null) {
155 for(int i
= container
.getComponentCount() - 1; i
>= 0 ; i
--){
156 if(component
.equals(container
.getComponent(i
))){
164 public int compare(final NodeDescriptor descriptor1
, final NodeDescriptor descriptor2
) {
165 if (descriptor1
instanceof ComponentPtrDescriptor
&& descriptor2
instanceof ComponentPtrDescriptor
) {
166 final RadComponent component1
= ((ComponentPtrDescriptor
)descriptor1
).getComponent();
167 final RadComponent component2
= ((ComponentPtrDescriptor
)descriptor2
).getComponent();
168 if (component1
== null || component2
== null) {
171 final RadContainer container1
= component1
.getParent();
172 final RadContainer container2
= component2
.getParent();
173 if(Comparing
.equal(container1
, container2
)){
174 return indexOf(container1
, component1
) - indexOf(container2
, component2
);
186 * Synchronizes tree with GuiEditor
188 private final class MyHierarchyChangeListener
implements HierarchyChangeListener
{
189 public void hierarchyChanged(){
190 if(myInsideChange
>0){
197 // After updating the tree we have to synchronize the selection in the tree
198 // with selected elemenet in the hierarchy
207 * Synchronizes selection in the tree with selection in the editor
209 private final class MySelectionWatcher
extends SelectionWatcher
{
210 public MySelectionWatcher(final GuiEditor editor
) {
214 protected void selectionChanged(final RadComponent component
, final boolean ignored
) {
219 private void updateSelection() {
220 final PropertyInspector propertyInspector
= UIDesignerToolWindowManager
.getInstance(myEditor
.getProject()).getPropertyInspector();
221 if (propertyInspector
.isEditing()) {
222 propertyInspector
.stopEditing();
225 if(myInsideChange
> 0){
238 * Synchronizes GuiEditor with the tree
240 private final class MyTreeSelectionListener
implements TreeSelectionListener
{
241 public void valueChanged(final TreeSelectionEvent e
) {
242 if (myInsideChange
>0) {
246 final Set
<ComponentPtr
> selectedElements
= getSelectedElements(ComponentPtr
.class);
249 FormEditingUtil
.clearSelection(myEditor
.getRootContainer());
250 boolean hasComponentInTab
= false;
252 for(ComponentPtr ptr
: selectedElements
) {
254 final RadComponent component
=ptr
.getComponent();
255 LOG
.assertTrue(component
!=null);
256 if (!hasComponentInTab
) {
257 hasComponentInTab
= FormEditingUtil
.selectComponent(myEditor
, component
);
260 component
.setSelected(true);
262 if (++count
== selectedElements
.size()) {
263 myEditor
.scrollComponentInView(component
);
268 // Notify ComponentTree that selected component changed
269 myEditor
.fireSelectedComponentChanged();