[i18n] idea-ui
[idea/community.git] / java / idea-ui / src / com / intellij / util / ui / classpath / ChooseLibrariesDialogBase.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.util.ui.classpath;
17
18 import com.intellij.ide.CommonActionsManager;
19 import com.intellij.ide.DefaultTreeExpander;
20 import com.intellij.ide.JavaUiBundle;
21 import com.intellij.ide.TreeExpander;
22 import com.intellij.ide.projectView.PresentationData;
23 import com.intellij.ide.util.treeView.AbstractTreeBuilder;
24 import com.intellij.ide.util.treeView.AbstractTreeStructure;
25 import com.intellij.ide.util.treeView.NodeDescriptor;
26 import com.intellij.openapi.actionSystem.ActionManager;
27 import com.intellij.openapi.actionSystem.ActionPlaces;
28 import com.intellij.openapi.actionSystem.DefaultActionGroup;
29 import com.intellij.openapi.application.Application;
30 import com.intellij.openapi.application.ApplicationManager;
31 import com.intellij.openapi.module.Module;
32 import com.intellij.openapi.module.ModuleManager;
33 import com.intellij.openapi.module.ModuleType;
34 import com.intellij.openapi.project.Project;
35 import com.intellij.openapi.project.ProjectManager;
36 import com.intellij.openapi.roots.LibraryOrderEntry;
37 import com.intellij.openapi.roots.ModuleRootManager;
38 import com.intellij.openapi.roots.OrderEntry;
39 import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil;
40 import com.intellij.openapi.roots.libraries.Library;
41 import com.intellij.openapi.roots.libraries.LibraryTable;
42 import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
43 import com.intellij.openapi.roots.ui.CellAppearanceEx;
44 import com.intellij.openapi.roots.ui.OrderEntryAppearanceService;
45 import com.intellij.openapi.ui.DialogWrapper;
46 import com.intellij.openapi.util.Disposer;
47 import com.intellij.openapi.util.NlsContexts;
48 import com.intellij.openapi.util.text.StringUtil;
49 import com.intellij.ui.DoubleClickListener;
50 import com.intellij.ui.ScrollPaneFactory;
51 import com.intellij.ui.SimpleColoredComponent;
52 import com.intellij.ui.SimpleTextAttributes;
53 import com.intellij.ui.treeStructure.SimpleNode;
54 import com.intellij.ui.treeStructure.SimpleTree;
55 import com.intellij.ui.treeStructure.SimpleTreeBuilder;
56 import com.intellij.ui.treeStructure.WeightBasedComparator;
57 import com.intellij.util.CommonProcessors;
58 import com.intellij.util.PlatformIcons;
59 import com.intellij.util.Processor;
60 import com.intellij.util.ui.JBUI;
61 import gnu.trove.THashMap;
62 import org.jetbrains.annotations.Nls;
63 import org.jetbrains.annotations.NotNull;
64 import org.jetbrains.annotations.Nullable;
65
66 import javax.swing.*;
67 import javax.swing.event.TreeSelectionEvent;
68 import javax.swing.event.TreeSelectionListener;
69 import javax.swing.tree.DefaultMutableTreeNode;
70 import javax.swing.tree.DefaultTreeModel;
71 import java.awt.*;
72 import java.awt.event.KeyEvent;
73 import java.awt.event.MouseEvent;
74 import java.util.List;
75 import java.util.*;
76
77 /**
78  * @author Gregory.Shrago
79  */
80
81 public abstract class ChooseLibrariesDialogBase extends DialogWrapper {
82   private final SimpleTree myTree = new SimpleTree();
83   private AbstractTreeBuilder myBuilder;
84   private List<Library> myResult;
85   private final Map<Object, Object> myParentsMap = new THashMap<>();
86
87   protected ChooseLibrariesDialogBase(final JComponent parentComponent, final @NlsContexts.DialogTitle String title) {
88     super(parentComponent, false);
89     setTitle(title);
90   }
91
92   protected ChooseLibrariesDialogBase(Project project, @NlsContexts.DialogTitle String title) {
93     super(project, false);
94     setTitle(title);
95   }
96
97   @Override
98   protected void init() {
99     super.init();
100     updateOKAction();
101   }
102
103   private static @Nls String notEmpty(@Nls String nodeText) {
104     return StringUtil.isNotEmpty(nodeText) ? nodeText : JavaUiBundle.message("unnamed.title");
105   }
106
107   @Override
108   protected String getDimensionServiceKey() {
109     return "#com.intellij.util.ui.classpath.ChooseLibrariesDialog";
110   }
111
112   protected int getLibraryTableWeight(@NotNull LibraryTable libraryTable) {
113     return 0;
114   }
115
116   protected boolean isAutoExpandLibraryTable(@NotNull LibraryTable libraryTable) {
117     return false;
118   }
119
120   @Override
121   protected void doOKAction() {
122     processSelection(new CommonProcessors.CollectProcessor<>(myResult = new ArrayList<>()));
123     super.doOKAction();
124   }
125
126   private void updateOKAction() {
127     setOKActionEnabled(!processSelection(new CommonProcessors.FindFirstProcessor<>()));
128   }
129
130   @Override
131   public JComponent getPreferredFocusedComponent() {
132     return myTree;
133   }
134
135   @NotNull
136   public List<Library> getSelectedLibraries() {
137     return myResult == null ? Collections.emptyList() : myResult;
138   }
139
140   protected void queueUpdateAndSelect(@NotNull final Library library) {
141     myBuilder.queueUpdate().doWhenDone(() -> myBuilder.select(library));
142   }
143
144   private boolean processSelection(final Processor<? super Library> processor) {
145     for (Object element : myBuilder.getSelectedElements()) {
146       if (element instanceof Library) {
147         if (!processor.process((Library)element)) return false;
148       }
149     }
150     return true;
151   }
152
153   protected boolean acceptsElement(final Object element) {
154     return true;
155   }
156
157   @Override
158   protected JComponent createNorthPanel() {
159     final DefaultActionGroup group = new DefaultActionGroup();
160     final TreeExpander expander = new DefaultTreeExpander(myTree);
161     final CommonActionsManager actionsManager = CommonActionsManager.getInstance();
162     group.add(actionsManager.createExpandAllAction(expander, myTree));
163     group.add(actionsManager.createCollapseAllAction(expander, myTree));
164     final JComponent component = ActionManager.getInstance().createActionToolbar(ActionPlaces.PROJECT_VIEW_TOOLBAR, group, true).getComponent();
165     component.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.darkGray), component.getBorder()));
166     return component;
167   }
168
169   @Override
170   @Nullable
171   protected JComponent createCenterPanel() {
172     myBuilder = new SimpleTreeBuilder(myTree, new DefaultTreeModel(new DefaultMutableTreeNode()),
173                                         new MyStructure(getProject()),
174                                         WeightBasedComparator.FULL_INSTANCE) {
175       // unique class to simplify search through the logs
176     };
177     myBuilder.initRootNode();
178
179     myTree.setDragEnabled(false);
180
181     myTree.setShowsRootHandles(true);
182     myTree.setRootVisible(false);
183     myTree.addTreeSelectionListener(new TreeSelectionListener() {
184       @Override
185       public void valueChanged(final TreeSelectionEvent e) {
186         updateOKAction();
187       }
188     });
189     new DoubleClickListener() {
190       @Override
191       protected boolean onDoubleClick(@NotNull MouseEvent e) {
192         if (isOKActionEnabled()) {
193           doOKAction();
194           return true;
195         }
196         return false;
197       }
198     }.installOn(myTree);
199
200     myTree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "ENTER");
201     myTree.getActionMap().put("ENTER", getOKAction());
202     final JScrollPane pane = ScrollPaneFactory.createScrollPane(myTree);
203     pane.setPreferredSize(JBUI.size(500, 400));
204     return pane;
205   }
206
207   @NotNull
208   protected Project getProject() {
209     return ProjectManager.getInstance().getDefaultProject();
210   }
211
212   protected LibrariesTreeNodeBase<Library> createLibraryDescriptor(NodeDescriptor parentDescriptor, Library library) {
213     return new LibraryDescriptor(getProject(), parentDescriptor, library);
214   }
215
216   protected void collectChildren(Object element, final List<Object> result) {
217     if (element instanceof Application) {
218       Collections.addAll(result, ProjectManager.getInstance().getOpenProjects());
219       final LibraryTablesRegistrar instance = LibraryTablesRegistrar.getInstance();
220       result.add(instance.getLibraryTable()); //1
221       result.addAll(instance.getCustomLibraryTables()); //2
222     }
223     else if (element instanceof Project) {
224       Collections.addAll(result, ModuleManager.getInstance((Project)element).getModules());
225       result.add(LibraryTablesRegistrar.getInstance().getLibraryTable((Project)element));
226     }
227     else if (element instanceof LibraryTable) {
228       Collections.addAll(result, ((LibraryTable)element).getLibraries());
229     }
230     else if (element instanceof Module) {
231       for (OrderEntry entry : ModuleRootManager.getInstance((Module)element).getOrderEntries()) {
232         if (entry instanceof LibraryOrderEntry) {
233           final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)entry;
234           if (LibraryTableImplUtil.MODULE_LEVEL.equals(libraryOrderEntry.getLibraryLevel())) {
235             final Library library = libraryOrderEntry.getLibrary();
236             result.add(library);
237           }
238         }
239       }
240     }
241   }
242
243   @Override
244   protected void dispose() {
245     Disposer.dispose(myBuilder);
246     super.dispose();
247   }
248
249   protected static class LibrariesTreeNodeBase<T> extends SimpleNode {
250     private final T myElement;
251
252     protected LibrariesTreeNodeBase(Project project, NodeDescriptor parentDescriptor, T element) {
253       super(project, parentDescriptor);
254       myElement = element;
255     }
256
257     @Override
258     public T getElement() {
259       return myElement;
260     }
261
262     @Override
263     public SimpleNode @NotNull [] getChildren() {
264       return NO_CHILDREN;
265     }
266
267     @Override
268     public int getWeight() {
269       return 0;
270     }
271
272     @Override
273     public Object @NotNull [] getEqualityObjects() {
274       return new Object[] {myElement};
275     }
276
277     @Override
278     protected void update(@NotNull PresentationData presentation) {
279       //todo[nik] this is workaround for bug in getTemplatePresentation().setIcons()
280       presentation.setIcon(getTemplatePresentation().getIcon(false));
281     }
282   }
283
284   private static class RootDescriptor extends LibrariesTreeNodeBase<Object> {
285     protected RootDescriptor(final Project project) {
286       super(project, null, ApplicationManager.getApplication());
287     }
288   }
289
290   private static class ProjectDescriptor extends LibrariesTreeNodeBase<Project> {
291     protected ProjectDescriptor(final Project project, final Project element) {
292       super(project, null, element);
293       getTemplatePresentation().setIcon(PlatformIcons.PROJECT_ICON);
294       getTemplatePresentation().addText(notEmpty(getElement().getName()), SimpleTextAttributes.REGULAR_ATTRIBUTES);
295     }
296   }
297
298   private static class ModuleDescriptor extends LibrariesTreeNodeBase<Module> {
299     protected ModuleDescriptor(final Project project, final NodeDescriptor parentDescriptor, final Module element) {
300       super(project, parentDescriptor, element);
301       final PresentationData templatePresentation = getTemplatePresentation();
302       templatePresentation.setIcon(ModuleType.get(element).getIcon());
303       templatePresentation.addText(notEmpty(element.getName()), SimpleTextAttributes.REGULAR_ATTRIBUTES);
304     }
305
306     @Override
307     public int getWeight() {
308       return 1;
309     }
310   }
311
312   private static class LibraryDescriptor extends LibrariesTreeNodeBase<Library> {
313     protected LibraryDescriptor(final Project project, final NodeDescriptor parentDescriptor, final Library element) {
314       super(project, parentDescriptor, element);
315       final CellAppearanceEx appearance = OrderEntryAppearanceService.getInstance().forLibrary(project, element, false);
316       final SimpleColoredComponent coloredComponent = new SimpleColoredComponent();
317       appearance.customize(coloredComponent);
318       final PresentationData templatePresentation = getTemplatePresentation();
319       templatePresentation.setIcon(coloredComponent.getIcon());
320       templatePresentation.addText(notEmpty(appearance.getText()), SimpleTextAttributes.REGULAR_ATTRIBUTES);
321     }
322   }
323
324   private static class LibraryTableDescriptor extends LibrariesTreeNodeBase<LibraryTable> {
325     private final int myWeight;
326     private final boolean myAutoExpand;
327
328     protected LibraryTableDescriptor(final Project project,
329                                      final NodeDescriptor parentDescriptor,
330                                      final LibraryTable table,
331                                      final int weight,
332                                      boolean autoExpand) {
333       super(project, parentDescriptor, table);
334       myWeight = weight;
335       myAutoExpand = autoExpand;
336       getTemplatePresentation().setIcon(PlatformIcons.LIBRARY_ICON);
337       final String nodeText = table.getPresentation().getDisplayName(true);
338       getTemplatePresentation().addText(notEmpty(nodeText), SimpleTextAttributes.REGULAR_ATTRIBUTES);
339     }
340
341     @Override
342     public boolean isAutoExpandNode() {
343       return myAutoExpand;
344     }
345
346     @Override
347     public int getWeight() {
348       return myWeight;
349     }
350   }
351
352   public boolean isEmpty() {
353     List<Object> children = new ArrayList<>();
354     final AbstractTreeStructure structure = myBuilder.getTreeStructure();
355     if (structure != null) collectChildren(structure.getRootElement(), children);
356     return children.isEmpty();
357   }
358
359   private class MyStructure extends AbstractTreeStructure {
360     private final Project myProject;
361
362     MyStructure(Project project) {
363       myProject = project;
364     }
365
366     @NotNull
367     @Override
368     public Object getRootElement() {
369       return ApplicationManager.getApplication();
370     }
371
372     @Override
373     public Object @NotNull [] getChildElements(@NotNull Object element) {
374       final List<Object> result = new ArrayList<>();
375       collectChildren(element, result);
376       final Iterator<Object> it = result.iterator();
377       while (it.hasNext()) {
378         if (!acceptsElement(it.next())) it.remove();
379       }
380       for (Object o : result) {
381         myParentsMap.put(o, element);
382       }
383       return result.toArray();
384     }
385
386     @Override
387     public Object getParentElement(@NotNull Object element) {
388       if (element instanceof Application) return null;
389       if (element instanceof Project) return ApplicationManager.getApplication();
390       if (element instanceof Module) return ((Module)element).getProject();
391       if (element instanceof LibraryTable) return myParentsMap.get(element);
392       if (element instanceof Library) return myParentsMap.get(element);
393       throw new AssertionError();
394     }
395
396     @NotNull
397     @Override
398     public NodeDescriptor createDescriptor(@NotNull Object element, NodeDescriptor parentDescriptor) {
399       if (element instanceof Application) return new RootDescriptor(myProject);
400       if (element instanceof Project) return new ProjectDescriptor(myProject, (Project)element);
401       if (element instanceof Module) return new ModuleDescriptor(myProject, parentDescriptor, (Module)element);
402       if (element instanceof LibraryTable) {
403         final LibraryTable libraryTable = (LibraryTable)element;
404         return new LibraryTableDescriptor(myProject, parentDescriptor, libraryTable,
405                                           getLibraryTableWeight(libraryTable),
406                                           isAutoExpandLibraryTable(libraryTable));
407       }
408       if (element instanceof Library) return createLibraryDescriptor(parentDescriptor, (Library)element);
409       throw new AssertionError();
410     }
411
412     @Override
413     public void commit() {
414     }
415
416     @Override
417     public boolean hasSomethingToCommit() {
418       return false;
419     }
420   }
421 }