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