update copyright
[fedora-idea.git] / java / idea-ui / src / com / intellij / openapi / roots / ui / configuration / projectRoot / BaseStructureConfigurable.java
blob4e9f45d5998dd94453dd1cad1421a88c76777ec3
1 /*
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.openapi.roots.ui.configuration.projectRoot;
18 import com.intellij.facet.Facet;
19 import com.intellij.ide.CommonActionsManager;
20 import com.intellij.ide.TreeExpander;
21 import com.intellij.ide.impl.convert.ProjectFileVersion;
22 import com.intellij.openapi.Disposable;
23 import com.intellij.openapi.actionSystem.*;
24 import com.intellij.openapi.keymap.Keymap;
25 import com.intellij.openapi.keymap.KeymapManager;
26 import com.intellij.openapi.module.Module;
27 import com.intellij.openapi.options.Configurable;
28 import com.intellij.openapi.options.SearchableConfigurable;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.project.ProjectBundle;
31 import com.intellij.openapi.projectRoots.Sdk;
32 import com.intellij.openapi.roots.libraries.Library;
33 import com.intellij.openapi.roots.libraries.LibraryTable;
34 import com.intellij.openapi.ui.MasterDetailsComponent;
35 import com.intellij.openapi.ui.NamedConfigurable;
36 import com.intellij.openapi.util.ActionCallback;
37 import com.intellij.openapi.util.Condition;
38 import com.intellij.ui.ColoredTreeCellRenderer;
39 import com.intellij.ui.SimpleTextAttributes;
40 import com.intellij.ui.TreeSpeedSearch;
41 import com.intellij.ui.TreeToolTipHandler;
42 import com.intellij.ui.awt.RelativePoint;
43 import com.intellij.ui.navigation.Place;
44 import com.intellij.util.Icons;
45 import com.intellij.util.StringBuilderSpinAllocator;
46 import com.intellij.util.containers.Convertor;
47 import com.intellij.util.ui.UIUtil;
48 import com.intellij.util.ui.tree.TreeUtil;
49 import com.intellij.packaging.artifacts.Artifact;
50 import org.jetbrains.annotations.NotNull;
51 import org.jetbrains.annotations.Nullable;
53 import javax.swing.*;
54 import javax.swing.tree.TreePath;
55 import java.awt.*;
56 import java.util.*;
57 import java.util.List;
59 public abstract class BaseStructureConfigurable extends MasterDetailsComponent implements SearchableConfigurable, Disposable, Configurable.Assistant, Place.Navigator {
61 protected StructureConfigurableContext myContext;
63 protected final Project myProject;
65 protected boolean myUiDisposed = true;
67 private boolean myWasTreeInitialized;
69 protected boolean myAutoScrollEnabled = true;
71 protected BaseStructureConfigurable(final Project project) {
72 myProject = project;
75 public void init(StructureConfigurableContext context) {
76 myContext = context;
77 myContext.addCacheUpdateListener(new Runnable() {
78 public void run() {
79 if (!myTree.isShowing()) return;
81 myTree.revalidate();
82 myTree.repaint();
84 });
88 public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) {
89 if (place == null) return new ActionCallback.Done();
91 final Object object = place.getPath(TREE_OBJECT);
92 final String byName = (String)place.getPath(TREE_NAME);
94 if (object == null && byName == null) return new ActionCallback.Done();
96 final MyNode node = object == null ? null : findNodeByObject(myRoot, object);
97 final MyNode nodeByName = byName == null ? null : findNodeByName(myRoot, byName);
99 if (node == null && nodeByName == null) return new ActionCallback.Done();
101 final NamedConfigurable config;
102 if (node != null) {
103 config = node.getConfigurable();
104 } else {
105 config = nodeByName.getConfigurable();
108 final ActionCallback result = new ActionCallback().doWhenDone(new Runnable() {
109 public void run() {
110 myAutoScrollEnabled = true;
114 myAutoScrollEnabled = false;
115 myAutoScrollHandler.cancelAllRequests();
116 final MyNode nodeToSelect = node != null ? node : nodeByName;
117 selectNodeInTree(nodeToSelect, requestFocus).doWhenDone(new Runnable() {
118 public void run() {
119 setSelectedNode(nodeToSelect);
120 Place.goFurther(config, place, requestFocus).notifyWhenDone(result);
124 return result;
128 public void queryPlace(@NotNull final Place place) {
129 if (myCurrentConfigurable != null) {
130 place.putPath(TREE_OBJECT, myCurrentConfigurable.getEditableObject());
131 Place.queryFurther(myCurrentConfigurable, place);
135 protected void initTree() {
136 if (myWasTreeInitialized) return;
137 myWasTreeInitialized = true;
139 super.initTree();
140 new TreeSpeedSearch(myTree, new Convertor<TreePath, String>() {
141 public String convert(final TreePath treePath) {
142 return ((MyNode)treePath.getLastPathComponent()).getDisplayName();
144 }, true);
145 TreeToolTipHandler.install(myTree);
146 ToolTipManager.sharedInstance().registerComponent(myTree);
147 myTree.setCellRenderer(new ColoredTreeCellRenderer(){
148 public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
149 if (value instanceof MyNode) {
150 final MyNode node = (MyNode)value;
152 if (node.getConfigurable() == null) {
153 return;
156 final String displayName = node.getDisplayName();
157 final Icon icon = node.getConfigurable().getIcon(expanded);
158 setIcon(icon);
159 setToolTipText(null);
160 setFont(UIUtil.getTreeFont());
161 if (node.isDisplayInBold()){
162 append(displayName, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
163 } else {
164 final Object object = node.getConfigurable().getEditableObject();
165 final boolean unused = myContext.isUnused(object);
166 final StructureConfigurableContext.ValidityLevel level = myContext.isInvalid(object);
167 final boolean invalid = level != StructureConfigurableContext.ValidityLevel.VALID;
168 if (unused || invalid){
169 Color fg = unused
170 ? UIUtil.getTextInactiveTextColor()
171 : selected && hasFocus ? UIUtil.getTreeSelectionForeground() : UIUtil.getTreeForeground();
172 append(displayName, new SimpleTextAttributes(invalid ? SimpleTextAttributes.STYLE_WAVED : SimpleTextAttributes.STYLE_PLAIN,
174 level == StructureConfigurableContext.ValidityLevel.ERROR ? Color.RED : Color.GRAY));
175 setToolTipText(composeTooltipMessage(invalid, object, displayName, unused));
177 else {
178 append(displayName, selected && hasFocus ? SimpleTextAttributes.SELECTED_SIMPLE_CELL_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES);
188 private String composeTooltipMessage(final boolean invalid, final Object object, final String displayName, final boolean unused) {
189 final StringBuilder buf = StringBuilderSpinAllocator.alloc();
190 try {
191 if (invalid) {
192 if (object instanceof Module) {
193 final Module module = (Module)object;
194 final Map<String, Set<String>> problems = myContext.myValidityCache.get(module);
196 if (problems.containsKey(StructureConfigurableContext.DUPLICATE_MODULE_NAME)) {
197 buf.append(StructureConfigurableContext.DUPLICATE_MODULE_NAME).append("\n");
200 if (problems.containsKey(StructureConfigurableContext.NO_JDK)){
201 buf.append(StructureConfigurableContext.NO_JDK).append("\n");
203 final Set<String> deletedLibraries = problems.get(StructureConfigurableContext.DELETED_LIBRARIES);
204 if (deletedLibraries != null) {
205 buf.append(ProjectBundle.message("project.roots.library.problem.message", deletedLibraries.size()));
206 for (String problem : deletedLibraries) {
207 if (deletedLibraries.size() > 1) {
208 buf.append(" - ");
210 buf.append("\'").append(problem).append("\'").append("\n");
213 } else {
214 buf.append(ProjectBundle.message("project.roots.tooltip.library.misconfigured", displayName)).append("\n");
217 if (unused) {
218 buf.append(ProjectBundle.message("project.roots.tooltip.unused", displayName));
220 return buf.toString();
222 finally {
223 StringBuilderSpinAllocator.dispose(buf);
227 public void disposeUIResources() {
228 if (myUiDisposed) return;
230 super.disposeUIResources();
232 myUiDisposed = true;
234 myAutoScrollHandler.cancelAllRequests();
235 myContext.myUpdateDependenciesAlarm.cancelAllRequests();
236 myContext.myUpdateDependenciesAlarm.addRequest(new Runnable(){
237 public void run() {
238 SwingUtilities.invokeLater(new Runnable(){
239 public void run() {
240 dispose();
244 }, 0);
247 protected void addCollapseExpandActions(final List<AnAction> result) {
248 final TreeExpander expander = new TreeExpander() {
249 public void expandAll() {
250 TreeUtil.expandAll(myTree);
253 public boolean canExpand() {
254 return true;
257 public void collapseAll() {
258 TreeUtil.collapseAll(myTree, 0);
261 public boolean canCollapse() {
262 return true;
265 final CommonActionsManager actionsManager = CommonActionsManager.getInstance();
266 result.add(actionsManager.createExpandAllAction(expander, myTree));
267 result.add(actionsManager.createCollapseAllAction(expander, myTree));
270 private class MyFindUsagesAction extends FindUsagesInProjectStructureActionBase {
272 public MyFindUsagesAction(JComponent parentComponent) {
273 super(parentComponent, myProject);
276 protected boolean isEnabled() {
277 final TreePath selectionPath = myTree.getSelectionPath();
278 if (selectionPath != null){
279 final MyNode node = (MyNode)selectionPath.getLastPathComponent();
280 return !node.isDisplayInBold();
281 } else {
282 return false;
286 protected StructureConfigurableContext getContext() {
287 return myContext;
290 protected Object getSelectedObject() {
291 return BaseStructureConfigurable.this.getSelectedObject();
294 protected RelativePoint getPointToShowResults() {
295 final int selectedRow = myTree.getSelectionRows()[0];
296 final Rectangle rowBounds = myTree.getRowBounds(selectedRow);
297 final Point location = rowBounds.getLocation();
298 location.x += rowBounds.width;
299 return new RelativePoint(myTree, location);
304 public void reset() {
305 myUiDisposed = false;
307 if (!myWasTreeInitialized) {
308 initTree();
309 myTree.setShowsRootHandles(false);
310 loadTree();
311 } else {
312 super.disposeUIResources();
313 myTree.setShowsRootHandles(false);
314 loadTree();
317 super.reset();
320 protected abstract void loadTree();
323 @NotNull
324 protected ArrayList<AnAction> createActions(final boolean fromPopup) {
325 final ArrayList<AnAction> result = new ArrayList<AnAction>();
326 AbstractAddGroup addAction = createAddAction();
327 if (addAction != null) {
328 result.add(addAction);
330 result.add(new MyRemoveAction());
332 final AnAction copyAction = createCopyAction();
333 if (copyAction != null) {
334 result.add(copyAction);
336 result.add(Separator.getInstance());
338 result.add(new MyFindUsagesAction(myTree));
341 return result;
344 @Nullable
345 protected AnAction createCopyAction() {
346 return null;
349 @Nullable
350 protected abstract AbstractAddGroup createAddAction();
352 protected class MyRemoveAction extends MyDeleteAction {
353 public MyRemoveAction() {
354 super(new Condition<Object>() {
355 public boolean value(final Object object) {
356 if (object instanceof MyNode) {
357 final NamedConfigurable namedConfigurable = ((MyNode)object).getConfigurable();
358 if (namedConfigurable != null) {
359 final Object editableObject = namedConfigurable.getEditableObject();
360 if (editableObject instanceof Sdk || editableObject instanceof Module || editableObject instanceof Facet || editableObject instanceof Artifact) return true;
361 if (editableObject instanceof Library) {
362 final LibraryTable table = ((Library)editableObject).getTable();
363 return table == null || table.isEditable();
367 return false;
372 public void actionPerformed(AnActionEvent e) {
373 final TreePath[] paths = myTree.getSelectionPaths();
374 if (paths == null) return;
376 final Set<TreePath> pathsToRemove = new HashSet<TreePath>();
377 for (TreePath path : paths) {
378 if (removeFromModel(path)) {
379 pathsToRemove.add(path);
382 removePaths(pathsToRemove.toArray(new TreePath[pathsToRemove.size()]));
385 private boolean removeFromModel(final TreePath selectionPath) {
386 final Object last = selectionPath.getLastPathComponent();
388 if (!(last instanceof MyNode)) return false;
390 final MyNode node = (MyNode)last;
391 final NamedConfigurable configurable = node.getConfigurable();
392 final Object editableObject = configurable.getEditableObject();
393 if (editableObject instanceof Sdk) {
394 removeJdk((Sdk)editableObject);
396 else if (editableObject instanceof Module) {
397 if (!removeModule((Module)editableObject)) return false;
399 else if (editableObject instanceof Facet) {
400 if (removeFacet((Facet)editableObject).isEmpty()) return false;
402 else if (editableObject instanceof Library) {
403 removeLibrary((Library)editableObject);
405 else if (editableObject instanceof Artifact) {
406 removeArtifact((Artifact)editableObject);
408 return true;
412 protected void removeArtifact(Artifact artifact) {
416 protected void removeLibrary(Library library) {
420 protected void removeFacetNodes(@NotNull List<Facet> facets) {
421 for (Facet facet : facets) {
422 MyNode node = findNodeByObject(myRoot, facet);
423 if (node != null) {
424 removePaths(TreeUtil.getPathFromRoot(node));
429 protected List<Facet> removeFacet(final Facet facet) {
430 return myContext.myModulesConfigurator.getFacetsConfigurator().removeFacet(facet);
433 protected boolean removeModule(final Module module) {
434 return true;
437 protected void removeJdk(final Sdk editableObject) {
440 protected abstract static class AbstractAddGroup extends ActionGroup implements ActionGroupWithPreselection {
442 protected AbstractAddGroup(String text, Icon icon) {
443 super(text, true);
445 final Presentation presentation = getTemplatePresentation();
446 presentation.setIcon(icon);
448 final Keymap active = KeymapManager.getInstance().getActiveKeymap();
449 if (active != null) {
450 final Shortcut[] shortcuts = active.getShortcuts("NewElement");
451 setShortcutSet(new CustomShortcutSet(shortcuts));
455 public AbstractAddGroup(String text) {
456 this(text, Icons.ADD_ICON);
459 public ActionGroup getActionGroup() {
460 return this;
463 public int getDefaultIndex() {
464 return 0;