CPP-13710 - commit only current document if there are no processors that can access...
[idea/community.git] / java / idea-ui / src / com / intellij / openapi / roots / ui / configuration / artifacts / LayoutTreeComponent.java
1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.openapi.roots.ui.configuration.artifacts;
3
4 import com.intellij.ide.JavaUiBundle;
5 import com.intellij.ide.dnd.DnDEvent;
6 import com.intellij.ide.dnd.DnDManager;
7 import com.intellij.ide.dnd.DnDTarget;
8 import com.intellij.openapi.Disposable;
9 import com.intellij.openapi.application.ApplicationManager;
10 import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.ArtifactRootNode;
11 import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
12 import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingNodeSource;
13 import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingTreeNodeFactory;
14 import com.intellij.openapi.ui.Messages;
15 import com.intellij.openapi.util.Comparing;
16 import com.intellij.openapi.util.Disposer;
17 import com.intellij.openapi.util.text.StringUtil;
18 import com.intellij.packaging.artifacts.Artifact;
19 import com.intellij.packaging.artifacts.ArtifactType;
20 import com.intellij.packaging.elements.CompositePackagingElement;
21 import com.intellij.packaging.elements.PackagingElement;
22 import com.intellij.packaging.elements.PackagingElementFactory;
23 import com.intellij.packaging.elements.PackagingElementType;
24 import com.intellij.packaging.impl.elements.DirectoryPackagingElement;
25 import com.intellij.packaging.ui.ArtifactEditorContext;
26 import com.intellij.packaging.ui.PackagingElementPropertiesPanel;
27 import com.intellij.packaging.ui.PackagingSourceItem;
28 import com.intellij.ui.ScrollPaneFactory;
29 import com.intellij.ui.awt.RelativeRectangle;
30 import com.intellij.ui.border.CustomLineBorder;
31 import com.intellij.ui.tree.AsyncTreeModel;
32 import com.intellij.ui.tree.StructureTreeModel;
33 import com.intellij.ui.tree.TreeVisitor;
34 import com.intellij.ui.treeStructure.SimpleTreeStructure;
35 import com.intellij.ui.treeStructure.WeightBasedComparator;
36 import com.intellij.util.containers.ContainerUtil;
37 import com.intellij.util.io.URLUtil;
38 import com.intellij.util.ui.JBUI;
39 import com.intellij.util.ui.tree.TreeUtil;
40 import org.jetbrains.annotations.NonNls;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43 import org.jetbrains.annotations.TestOnly;
44 import org.jetbrains.concurrency.Promise;
45 import org.jetbrains.concurrency.Promises;
46
47 import javax.swing.*;
48 import javax.swing.event.TreeSelectionEvent;
49 import javax.swing.event.TreeSelectionListener;
50 import javax.swing.tree.DefaultMutableTreeNode;
51 import javax.swing.tree.TreePath;
52 import java.awt.*;
53 import java.util.List;
54 import java.util.*;
55 import java.util.function.Predicate;
56
57 public class LayoutTreeComponent implements DnDTarget, Disposable {
58   @NonNls private static final String EMPTY_CARD = "<empty>";
59   @NonNls private static final String PROPERTIES_CARD = "properties";
60   private final ArtifactEditorImpl myArtifactsEditor;
61   private final LayoutTree myTree;
62   private final JPanel myTreePanel;
63   private final ComplexElementSubstitutionParameters mySubstitutionParameters;
64   private final ArtifactEditorContext myContext;
65   private final Artifact myOriginalArtifact;
66   private final StructureTreeModel<LayoutTreeStructure> myStructureTreeModel;
67   private SelectedElementInfo<?> mySelectedElementInfo = new SelectedElementInfo<>(null);
68   private JPanel myPropertiesPanelWrapper;
69   private JPanel myPropertiesPanel;
70   private boolean mySortElements;
71   private final LayoutTreeStructure myTreeStructure;
72
73   public LayoutTreeComponent(ArtifactEditorImpl artifactsEditor, ComplexElementSubstitutionParameters substitutionParameters,
74                              ArtifactEditorContext context, Artifact originalArtifact, boolean sortElements) {
75     myArtifactsEditor = artifactsEditor;
76     mySubstitutionParameters = substitutionParameters;
77     myContext = context;
78     myOriginalArtifact = originalArtifact;
79     mySortElements = sortElements;
80     myTreeStructure = new LayoutTreeStructure();
81     myStructureTreeModel = new StructureTreeModel<>(myTreeStructure, getComparator(), this);
82     myTree = new LayoutTree(myArtifactsEditor, myStructureTreeModel);
83     myTree.setModel(new AsyncTreeModel(myStructureTreeModel, this));
84     Disposer.register(this, myTree);
85
86     myTree.addTreeSelectionListener(new TreeSelectionListener() {
87       @Override
88       public void valueChanged(TreeSelectionEvent e) {
89         updatePropertiesPanel(false);
90       }
91     });
92     createPropertiesPanel();
93     myTreePanel = new JPanel(new BorderLayout());
94     myTreePanel.add(ScrollPaneFactory.createScrollPane(myTree), BorderLayout.CENTER);
95     myTreePanel.add(myPropertiesPanelWrapper, BorderLayout.SOUTH);
96     if (!ApplicationManager.getApplication().isUnitTestMode()) {
97       DnDManager.getInstance().registerTarget(this, myTree);
98     }
99   }
100
101   @Nullable
102   private WeightBasedComparator getComparator() {
103     return mySortElements ? new WeightBasedComparator(true) : null;
104   }
105
106   public void setSortElements(boolean sortElements) {
107     mySortElements = sortElements;
108     myStructureTreeModel.setComparator(getComparator());
109     myArtifactsEditor.getContext().getParent().getDefaultSettings().setSortElements(sortElements);
110   }
111
112   @Nullable
113   private static PackagingElementNode getNode(Object value) {
114     if (!(value instanceof DefaultMutableTreeNode)) return null;
115     final Object userObject = ((DefaultMutableTreeNode)value).getUserObject();
116     return userObject instanceof PackagingElementNode ? (PackagingElementNode)userObject : null;
117   }
118
119   private void createPropertiesPanel() {
120     myPropertiesPanel = new JPanel(new BorderLayout());
121     final JPanel emptyPanel = new JPanel();
122     emptyPanel.setMinimumSize(JBUI.emptySize());
123     emptyPanel.setPreferredSize(JBUI.emptySize());
124
125     myPropertiesPanelWrapper = new JPanel(new CardLayout());
126     myPropertiesPanel.setBorder(new CustomLineBorder(1, 0, 0, 0));
127     myPropertiesPanelWrapper.add(EMPTY_CARD, emptyPanel);
128     myPropertiesPanelWrapper.add(PROPERTIES_CARD, myPropertiesPanel);
129   }
130
131   public Artifact getArtifact() {
132     return myArtifactsEditor.getArtifact();
133   }
134
135   public LayoutTree getLayoutTree() {
136     return myTree;
137   }
138
139   public void updatePropertiesPanel(final boolean force) {
140     final PackagingElement<?> selected = getSelection().getElementIfSingle();
141     if (!force && Comparing.equal(selected, mySelectedElementInfo.myElement)) {
142       return;
143     }
144     mySelectedElementInfo.save();
145     mySelectedElementInfo = new SelectedElementInfo<PackagingElement<?>>(selected);
146     mySelectedElementInfo.showPropertiesPanel();
147   }
148
149   public void saveElementProperties() {
150     mySelectedElementInfo.save();
151   }
152
153   public void rebuildTree() {
154     myTreeStructure.clearCaches();
155     myStructureTreeModel.invalidate();
156     updatePropertiesPanel(true);
157     myArtifactsEditor.queueValidation();
158   }
159
160   public LayoutTreeSelection getSelection() {
161     return myTree.getSelection();
162   }
163
164   public void addNewPackagingElement(@NotNull PackagingElementType<?> type) {
165     PackagingElementNode<?> parentNode = getParentNode(myTree.getSelection());
166     final PackagingElement<?> element = parentNode.getFirstElement();
167     final CompositePackagingElement<?> parent;
168     if (element instanceof CompositePackagingElement<?>) {
169       parent = (CompositePackagingElement<?>)element;
170     }
171     else {
172       parent = getArtifact().getRootElement();
173       parentNode = getRootNode();
174     }
175     if (!checkCanAdd(parent, parentNode)) return;
176
177     final List<? extends PackagingElement<?>> children = type.chooseAndCreate(myContext, getArtifact(), parent);
178     final PackagingElementNode<?> finalParentNode = parentNode;
179     editLayout(() -> {
180       CompositePackagingElement<?> actualParent = getOrCreateModifiableParent(parent, finalParentNode);
181       for (PackagingElement<?> child : children) {
182         actualParent.addOrFindChild(child);
183       }
184     });
185     updateAndSelect(parentNode, children);
186   }
187
188   private static CompositePackagingElement<?> getOrCreateModifiableParent(CompositePackagingElement<?> parentElement, PackagingElementNode<?> node) {
189     PackagingElementNode<?> current = node;
190     List<String> dirNames = new ArrayList<>();
191     while (current != null && !(current instanceof ArtifactRootNode)) {
192       final PackagingElement<?> packagingElement = current.getFirstElement();
193       if (!(packagingElement instanceof DirectoryPackagingElement)) {
194         return parentElement;
195       }
196       dirNames.add(((DirectoryPackagingElement)packagingElement).getDirectoryName());
197       current = current.getParentNode();
198     }
199
200     if (current == null) return parentElement;
201     final PackagingElement<?> rootElement = current.getElementIfSingle();
202     if (!(rootElement instanceof CompositePackagingElement<?>)) return parentElement;
203
204     Collections.reverse(dirNames);
205     String path = StringUtil.join(dirNames, "/");
206     return PackagingElementFactory.getInstance().getOrCreateDirectory((CompositePackagingElement<?>)rootElement, path);
207   }
208
209   public boolean checkCanModify(@NotNull PackagingElement<?> element, @NotNull PackagingElementNode<?> node) {
210     return checkCanModify(node.getNodeSource(element));
211   }
212
213   public boolean checkCanModifyChildren(@NotNull PackagingElement<?> parentElement,
214                                         @NotNull PackagingElementNode<?> parentNode,
215                                         @NotNull Collection<? extends PackagingElementNode<?>> children) {
216     final List<PackagingNodeSource> sources = new ArrayList<>(parentNode.getNodeSource(parentElement));
217     for (PackagingElementNode<?> child : children) {
218       sources.addAll(child.getNodeSources());
219     }
220     return checkCanModify(sources);
221   }
222
223   public boolean checkCanModify(final Collection<? extends PackagingNodeSource> nodeSources) {
224     if (nodeSources.isEmpty()) {
225       return true;
226     }
227
228     if (nodeSources.size() > 1) {
229       Messages.showErrorDialog(myArtifactsEditor.getMainComponent(),
230                                JavaUiBundle.message(
231                                  "error.message.the.selected.node.consist.of.several.elements.so.it.cannot.be.edited"));
232     }
233     else {
234     final PackagingNodeSource source = ContainerUtil.getFirstItem(nodeSources, null);
235       if (source != null) {
236         Messages.showErrorDialog(myArtifactsEditor.getMainComponent(),
237                                  JavaUiBundle.message(
238                                    "error.message.the.selected.node.belongs.to.0.element.so.it.cannot.be.edited",
239                                    source.getPresentableName()));
240       }
241     }
242     return false;
243
244   }
245
246   public boolean checkCanAdd(CompositePackagingElement<?> parentElement, PackagingElementNode<?> parentNode) {
247     boolean allParentsAreDirectories = true;
248     PackagingElementNode<?> current = parentNode;
249     while (current != null && !(current instanceof ArtifactRootNode)) {
250       final PackagingElement<?> element = current.getFirstElement();
251       if (!(element instanceof DirectoryPackagingElement)) {
252         allParentsAreDirectories = false;
253         break;
254       }
255       current = current.getParentNode();
256     }
257
258     return allParentsAreDirectories || checkCanModify(parentElement, parentNode);
259   }
260
261   public boolean checkCanRemove(final List<? extends PackagingElementNode<?>> nodes) {
262     Set<PackagingNodeSource> rootSources = new HashSet<>();
263     for (PackagingElementNode<?> node : nodes) {
264       rootSources.addAll(getRootNodeSources(node.getNodeSources()));
265     }
266
267     if (!rootSources.isEmpty()) {
268       final String name = rootSources.iterator().next().getPresentableName();
269       final String message = JavaUiBundle.message("layout.tree.check.can.remove.dialog.message", name, rootSources.size());
270       final int answer = Messages.showYesNoDialog(myArtifactsEditor.getMainComponent(), message, JavaUiBundle.message(
271         "dialog.title.remove.elements"), null);
272       if (answer != Messages.YES) return false;
273     }
274     return true;
275   }
276
277   public void updateAndSelect(PackagingElementNode<?> node, final List<? extends PackagingElement<?>> toSelect) {
278     myArtifactsEditor.queueValidation();
279     myTreeStructure.clearCaches();
280     List<PackagingElementNode<?>> nodesToSelect = Collections.synchronizedList(new ArrayList<>(toSelect.size()));
281     myStructureTreeModel.invalidate(node, true)
282       .thenAsync(result -> TreeUtil.promiseVisit(myTree, (path) -> {
283         Object nodeObject = TreeUtil.getLastUserObject(path);
284         if (nodeObject instanceof PackagingElementNode && ContainerUtil.intersects(((PackagingElementNode<?>)nodeObject).getPackagingElements(), toSelect)) {
285           nodesToSelect.add((PackagingElementNode)nodeObject);
286         }
287         return TreeVisitor.Action.CONTINUE;
288       }))
289       .thenAsync(result -> Promises.collectResults(ContainerUtil.map(nodesToSelect, nodeToSelect -> myStructureTreeModel.promiseVisitor(nodeToSelect))))
290       .thenAsync(visitors -> TreeUtil.promiseSelect(myTree, visitors.stream()));
291   }
292
293   public Promise<TreePath> selectNode(@NotNull String parentPath, @NotNull PackagingElement<?> element) {
294     Predicate<PackagingElementNode<?>> filter = node -> node.getPackagingElements().stream().anyMatch(element::isEqualTo);
295     return TreeUtil.promiseSelect(myTree, myTree.createVisitorCompositeNodeChild(parentPath, filter));
296   }
297
298   @TestOnly
299   public Promise<TreePath> selectNode(@NotNull String parentPath, @NotNull String nodeName) {
300     Predicate<PackagingElementNode<?>> filter = node -> node.getElementPresentation().getSearchName().equals(nodeName);
301     return TreeUtil.promiseSelect(myTree, myTree.createVisitorCompositeNodeChild(parentPath, filter));
302   }
303
304   public void editLayout(Runnable action) {
305     myContext.editLayout(myOriginalArtifact, action);
306   }
307
308   public void removeSelectedElements() {
309     final LayoutTreeSelection selection = myTree.getSelection();
310     if (!checkCanRemove(selection.getNodes())) return;
311
312     editLayout(() -> removeNodes(selection.getNodes()));
313
314     myArtifactsEditor.rebuildTries();
315   }
316
317   public void removeNodes(final List<? extends PackagingElementNode<?>> nodes) {
318     Set<PackagingElementNode<?>> parents = new HashSet<>();
319     for (PackagingElementNode<?> node : nodes) {
320       final List<? extends PackagingElement<?>> toDelete = node.getPackagingElements();
321       for (PackagingElement<?> element : toDelete) {
322         final Collection<PackagingNodeSource> nodeSources = node.getNodeSource(element);
323         if (nodeSources.isEmpty()) {
324           final CompositePackagingElement<?> parent = node.getParentElement(element);
325           if (parent != null) {
326             ContainerUtil.addIfNotNull(parents, node.getParentNode());
327             parent.removeChild(element);
328           }
329         }
330         else {
331           Collection<PackagingNodeSource> rootSources = getRootNodeSources(nodeSources);
332           for (PackagingNodeSource source : rootSources) {
333             parents.add(source.getSourceParentNode());
334             source.getSourceParentElement().removeChild(source.getSourceElement());
335           }
336         }
337       }
338     }
339     for (PackagingElementNode<?> parent : parents) {
340       myTree.addSubtreeToUpdate(parent);
341     }
342   }
343
344   private static Collection<PackagingNodeSource> getRootNodeSources(Collection<? extends PackagingNodeSource> nodeSources) {
345     Set<PackagingNodeSource> result = new HashSet<>();
346     collectRootNodeSources(nodeSources, result);
347     return result;
348   }
349
350   private static void collectRootNodeSources(Collection<? extends PackagingNodeSource> nodeSources, Set<? super PackagingNodeSource> result) {
351     for (PackagingNodeSource nodeSource : nodeSources) {
352       final Collection<PackagingNodeSource> parentSources = nodeSource.getParentSources();
353       if (parentSources.isEmpty()) {
354         result.add(nodeSource);
355       }
356       else {
357         collectRootNodeSources(parentSources, result);
358       }
359     }
360   }
361
362   private PackagingElementNode<?> getParentNode(final LayoutTreeSelection selection) {
363     final PackagingElementNode<?> node = selection.getNodeIfSingle();
364     if (node != null) {
365       if (node.getFirstElement() instanceof CompositePackagingElement) {
366         return node;
367       }
368       final PackagingElementNode<?> parent = node.getParentNode();
369       if (parent != null) {
370         return parent;
371       }
372     }
373     return getRootNode();
374   }
375
376   public JPanel getTreePanel() {
377     return myTreePanel;
378   }
379
380   @Override
381   public void dispose() {
382     if (!ApplicationManager.getApplication().isUnitTestMode()) {
383       DnDManager.getInstance().unregisterTarget(this, myTree);
384     }
385   }
386
387   @Override
388   public boolean update(DnDEvent aEvent) {
389     aEvent.setDropPossible(false);
390     aEvent.hideHighlighter();
391     final Object object = aEvent.getAttachedObject();
392     if (object instanceof PackagingElementDraggingObject) {
393       final DefaultMutableTreeNode parent = findParentCompositeElementNode(aEvent.getRelativePoint().getPoint(myTree));
394       if (parent != null) {
395         final PackagingElementDraggingObject draggingObject = (PackagingElementDraggingObject)object;
396         final PackagingElementNode node = getNode(parent);
397         if (node != null && draggingObject.canDropInto(node)) {
398           final PackagingElement element = node.getFirstElement();
399           if (element instanceof CompositePackagingElement) {
400             draggingObject.setTargetNode(node);
401             draggingObject.setTargetElement((CompositePackagingElement<?>)element);
402             final Rectangle bounds = myTree.getPathBounds(TreeUtil.getPathFromRoot(parent));
403             aEvent.setHighlighting(new RelativeRectangle(myTree, bounds), DnDEvent.DropTargetHighlightingType.RECTANGLE);
404             aEvent.setDropPossible(true);
405           }
406         }
407       }
408     }
409     return false;
410   }
411
412   @Override
413   public void drop(DnDEvent aEvent) {
414     final Object object = aEvent.getAttachedObject();
415     if (object instanceof PackagingElementDraggingObject) {
416       final PackagingElementDraggingObject draggingObject = (PackagingElementDraggingObject)object;
417       final PackagingElementNode<?> targetNode = draggingObject.getTargetNode();
418       final CompositePackagingElement<?> targetElement = draggingObject.getTargetElement();
419       if (targetElement == null || targetNode == null || !draggingObject.checkCanDrop()) return;
420       if (!checkCanAdd(targetElement, targetNode)) {
421         return;
422       }
423       final List<PackagingElement<?>> toSelect = new ArrayList<>();
424       editLayout(() -> {
425         draggingObject.beforeDrop();
426         final CompositePackagingElement<?> parent = getOrCreateModifiableParent(targetElement, targetNode);
427         for (PackagingElement<?> element : draggingObject.createPackagingElements(myContext)) {
428           toSelect.add(element);
429           parent.addOrFindChild(element);
430         }
431       });
432       updateAndSelect(targetNode, toSelect);
433       myArtifactsEditor.getSourceItemsTree().rebuildTree();
434     }
435   }
436
437   @Nullable
438   private DefaultMutableTreeNode findParentCompositeElementNode(Point point) {
439     TreePath path = myTree.getPathForLocation(point.x, point.y);
440     while (path != null) {
441       final PackagingElement<?> element = myTree.getElementByPath(path);
442       if (element instanceof CompositePackagingElement) {
443         return (DefaultMutableTreeNode)path.getLastPathComponent();
444       }
445       path = path.getParentPath();
446     }
447     return null;
448   }
449
450   public void startRenaming(TreePath path) {
451     myTree.startEditingAtPath(path);
452   }
453
454   public boolean isEditing() {
455     return myTree.isEditing();
456   }
457
458   public void setRootElement(CompositePackagingElement<?> rootElement) {
459     myContext.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact).setRootElement(rootElement);
460     myTreeStructure.updateRootElement();
461     rebuildTree();
462     myArtifactsEditor.getSourceItemsTree().rebuildTree();
463   }
464
465   public PackagingElementNode<?> getRootNode() {
466     return myTreeStructure.getRootNode();
467   }
468
469   @NotNull
470   public CompositePackagingElement<?> getRootElement() {
471     return myContext.getRootElement(myOriginalArtifact);
472   }
473
474   public void updateTreeNodesPresentation() {
475     myStructureTreeModel.invalidate();
476   }
477
478   public void updateRootNode() {
479     myStructureTreeModel.invalidate(myTreeStructure.getRootElement(), false);
480   }
481
482   public void initTree() {
483     mySelectedElementInfo.showPropertiesPanel();
484   }
485
486   public void putIntoDefaultLocations(@NotNull final List<? extends PackagingSourceItem> items) {
487     final List<PackagingElement<?>> toSelect = new ArrayList<>();
488     editLayout(() -> {
489       final CompositePackagingElement<?> rootElement = getArtifact().getRootElement();
490       final ArtifactType artifactType = getArtifact().getArtifactType();
491       for (PackagingSourceItem item : items) {
492         final String path = artifactType.getDefaultPathFor(item);
493         if (path != null) {
494           final CompositePackagingElement<?> parent;
495           if (path.endsWith(URLUtil.JAR_SEPARATOR)) {
496             parent = PackagingElementFactory.getInstance().getOrCreateArchive(rootElement, StringUtil.trimEnd(path, URLUtil.JAR_SEPARATOR));
497           }
498           else {
499             parent = PackagingElementFactory.getInstance().getOrCreateDirectory(rootElement, path);
500           }
501           final List<? extends PackagingElement<?>> elements = item.createElements(myContext);
502           toSelect.addAll(parent.addOrFindChildren(elements));
503         }
504       }
505     });
506
507     myArtifactsEditor.getSourceItemsTree().rebuildTree();
508     updateAndSelect(getRootNode(), toSelect);
509   }
510
511   public void putElements(@NotNull final String path, @NotNull final List<? extends PackagingElement<?>> elements) {
512     final List<PackagingElement<?>> toSelect = new ArrayList<>();
513     editLayout(() -> {
514       final CompositePackagingElement<?> directory =
515         PackagingElementFactory.getInstance().getOrCreateDirectory(getArtifact().getRootElement(), path);
516       toSelect.addAll(directory.addOrFindChildren(elements));
517     });
518     myArtifactsEditor.getSourceItemsTree().rebuildTree();
519     updateAndSelect(getRootNode(), toSelect);
520   }
521
522   public void packInto(@NotNull final List<? extends PackagingSourceItem> items, final String pathToJar) {
523     final List<PackagingElement<?>> toSelect = new ArrayList<>();
524     final CompositePackagingElement<?> rootElement = getArtifact().getRootElement();
525     editLayout(() -> {
526       final CompositePackagingElement<?> archive = PackagingElementFactory.getInstance().getOrCreateArchive(rootElement, pathToJar);
527       for (PackagingSourceItem item : items) {
528         final List<? extends PackagingElement<?>> elements = item.createElements(myContext);
529         archive.addOrFindChildren(elements);
530       }
531       toSelect.add(archive);
532     });
533
534     myArtifactsEditor.getSourceItemsTree().rebuildTree();
535     updateAndSelect(getRootNode(), toSelect);
536   }
537
538   public boolean isPropertiesModified() {
539     final PackagingElementPropertiesPanel panel = mySelectedElementInfo.myCurrentPanel;
540     return panel != null && panel.isModified();
541   }
542
543   public void resetElementProperties() {
544     final PackagingElementPropertiesPanel panel = mySelectedElementInfo.myCurrentPanel;
545     if (panel != null) {
546       panel.reset();
547     }
548   }
549
550   public boolean isSortElements() {
551     return mySortElements;
552   }
553
554   private final class SelectedElementInfo<E extends PackagingElement<?>> {
555     private final E myElement;
556     private PackagingElementPropertiesPanel myCurrentPanel;
557
558     private SelectedElementInfo(@Nullable E element) {
559       myElement = element;
560       if (myElement != null) {
561         //noinspection unchecked
562         myCurrentPanel = element.getType().createElementPropertiesPanel(myElement, myContext);
563         myPropertiesPanel.removeAll();
564         if (myCurrentPanel != null) {
565           myPropertiesPanel.add(BorderLayout.CENTER, ScrollPaneFactory.createScrollPane(myCurrentPanel.createComponent(), true));
566           myCurrentPanel.reset();
567           myPropertiesPanel.revalidate();
568         }
569       }
570     }
571
572     public void save() {
573       if (myCurrentPanel != null && myCurrentPanel.isModified()) {
574         editLayout(() -> myCurrentPanel.apply());
575       }
576     }
577
578     public void showPropertiesPanel() {
579       final CardLayout cardLayout = (CardLayout)myPropertiesPanelWrapper.getLayout();
580       if (myCurrentPanel != null) {
581         cardLayout.show(myPropertiesPanelWrapper, PROPERTIES_CARD);
582       }
583       else {
584         cardLayout.show(myPropertiesPanelWrapper, EMPTY_CARD);
585       }
586       myPropertiesPanelWrapper.repaint();
587     }
588   }
589
590   private class LayoutTreeStructure extends SimpleTreeStructure {
591     private ArtifactRootNode myRootNode;
592
593     @NotNull
594     @Override
595     public Object getRootElement() {
596       return getRootNode();
597     }
598
599     @NotNull
600     ArtifactRootNode getRootNode() {
601       if (myRootNode == null) {
602         myRootNode = PackagingTreeNodeFactory.createRootNode(LayoutTreeComponent.this.getRootElement(), myContext, mySubstitutionParameters, getArtifact().getArtifactType());
603       }
604       return myRootNode;
605     }
606
607     public void updateRootElement() {
608       myRootNode = null;
609     }
610   }
611 }