ComponentWithBrowseButton - optional remove listener on hide
[fedora-idea.git] / java / idea-ui / src / com / intellij / openapi / roots / ui / configuration / artifacts / ArtifactEditorImpl.java
blob058e1c427034023e23999d152d9918b514a734b2
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.artifacts;
18 import com.intellij.codeInsight.hint.HintManager;
19 import com.intellij.codeInsight.hint.HintUtil;
20 import com.intellij.ide.CommonActionsManager;
21 import com.intellij.ide.DataManager;
22 import com.intellij.ide.DefaultTreeExpander;
23 import com.intellij.ide.impl.TypeSafeDataProviderAdapter;
24 import com.intellij.openapi.actionSystem.*;
25 import com.intellij.openapi.compiler.CompilerBundle;
26 import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
27 import com.intellij.openapi.module.Module;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.project.ProjectBundle;
30 import com.intellij.openapi.roots.libraries.Library;
31 import com.intellij.openapi.roots.ui.configuration.artifacts.actions.*;
32 import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.LibrarySourceItem;
33 import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.ModuleOutputSourceItem;
34 import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemsTree;
35 import com.intellij.openapi.ui.FixedSizeButton;
36 import com.intellij.openapi.ui.Splitter;
37 import com.intellij.openapi.ui.TextFieldWithBrowseButton;
38 import com.intellij.openapi.util.Comparing;
39 import com.intellij.openapi.util.Disposer;
40 import com.intellij.openapi.util.IconLoader;
41 import com.intellij.openapi.util.io.FileUtil;
42 import com.intellij.openapi.vfs.VirtualFile;
43 import com.intellij.packaging.artifacts.Artifact;
44 import com.intellij.packaging.artifacts.ArtifactType;
45 import com.intellij.packaging.artifacts.ModifiableArtifact;
46 import com.intellij.packaging.elements.ComplexPackagingElementType;
47 import com.intellij.packaging.elements.CompositePackagingElement;
48 import com.intellij.packaging.elements.PackagingElementFactory;
49 import com.intellij.packaging.elements.PackagingElementType;
50 import com.intellij.packaging.impl.artifacts.ArtifactUtil;
51 import com.intellij.packaging.impl.elements.ArchivePackagingElement;
52 import com.intellij.packaging.impl.elements.ManifestFileUtil;
53 import com.intellij.packaging.ui.ManifestFileConfiguration;
54 import com.intellij.ui.*;
55 import com.intellij.ui.awt.RelativePoint;
56 import com.intellij.util.EventDispatcher;
57 import com.intellij.util.Icons;
58 import com.intellij.util.ui.ThreeStateCheckBox;
59 import org.jetbrains.annotations.NotNull;
60 import org.jetbrains.annotations.Nullable;
62 import javax.swing.*;
63 import javax.swing.border.Border;
64 import javax.swing.event.HyperlinkEvent;
65 import javax.swing.event.HyperlinkListener;
66 import java.awt.*;
67 import java.awt.event.ActionEvent;
68 import java.awt.event.ActionListener;
69 import java.util.ArrayList;
70 import java.util.Collections;
71 import java.util.List;
73 /**
74 * @author nik
76 public class ArtifactEditorImpl implements ArtifactEditorEx {
77 private JPanel myMainPanel;
78 private JCheckBox myBuildOnMakeCheckBox;
79 private TextFieldWithBrowseButton myOutputDirectoryField;
80 private JPanel myEditorPanel;
81 private JPanel myErrorPanelPlace;
82 private ThreeStateCheckBox myShowContentCheckBox;
83 private FixedSizeButton myShowSpecificContentOptionsButton;
84 private ActionGroup myShowSpecificContentOptionsGroup;
85 private final Project myProject;
86 private final ComplexElementSubstitutionParameters mySubstitutionParameters = new ComplexElementSubstitutionParameters();
87 private final EventDispatcher<ArtifactEditorListener> myDispatcher = EventDispatcher.create(ArtifactEditorListener.class);
88 private final ArtifactEditorContextImpl myContext;
89 private SourceItemsTree mySourceItemsTree;
90 private final Artifact myOriginalArtifact;
91 private final LayoutTreeComponent myLayoutTreeComponent;
92 private TabbedPaneWrapper myTabbedPane;
93 private ArtifactPropertiesEditors myPropertiesEditors;
94 private ArtifactValidationManagerImpl myValidationManager;
95 private boolean myDisposed;
97 public ArtifactEditorImpl(final @NotNull ArtifactsStructureConfigurableContext context, @NotNull Artifact artifact, @NotNull ArtifactEditorSettings settings) {
98 myContext = createArtifactEditorContext(context);
99 myOriginalArtifact = artifact;
100 myProject = context.getProject();
101 mySubstitutionParameters.setTypesToShowContent(settings.getTypesToShowContent());
102 mySourceItemsTree = new SourceItemsTree(myContext, this);
103 myLayoutTreeComponent = new LayoutTreeComponent(this, mySubstitutionParameters, myContext, myOriginalArtifact, settings.isSortElements());
104 myPropertiesEditors = new ArtifactPropertiesEditors(myContext, myOriginalArtifact, myOriginalArtifact);
105 Disposer.register(this, mySourceItemsTree);
106 Disposer.register(this, myLayoutTreeComponent);
107 myBuildOnMakeCheckBox.setSelected(artifact.isBuildOnMake());
108 final String outputPath = artifact.getOutputPath();
109 myOutputDirectoryField.addBrowseFolderListener(CompilerBundle.message("dialog.title.output.directory.for.artifact"),
110 CompilerBundle.message("chooser.description.select.output.directory.for.0.artifact",
111 getArtifact().getName()), myProject,
112 FileChooserDescriptorFactory.createSingleFolderDescriptor());
113 myShowSpecificContentOptionsGroup = createShowSpecificContentOptionsGroup();
114 myShowSpecificContentOptionsButton.addActionListener(new ActionListener() {
115 public void actionPerformed(ActionEvent e) {
116 ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, myShowSpecificContentOptionsGroup).getComponent().show(myShowSpecificContentOptionsButton, 0, 0);
119 setOutputPath(outputPath);
120 myValidationManager = new ArtifactValidationManagerImpl(this);
121 updateShowContentCheckbox();
124 protected ArtifactEditorContextImpl createArtifactEditorContext(ArtifactsStructureConfigurableContext parentContext) {
125 return new ArtifactEditorContextImpl(parentContext, this);
128 private ActionGroup createShowSpecificContentOptionsGroup() {
129 final DefaultActionGroup group = new DefaultActionGroup();
130 for (ComplexPackagingElementType<?> type : PackagingElementFactory.getInstance().getComplexElementTypes()) {
131 group.add(new ToggleShowElementContentAction(type, this));
133 return group;
136 private void setOutputPath(@Nullable String outputPath) {
137 myOutputDirectoryField.setText(outputPath != null ? FileUtil.toSystemDependentName(outputPath) : null);
140 public void apply() {
141 final ModifiableArtifact modifiableArtifact = myContext.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact);
142 modifiableArtifact.setBuildOnMake(myBuildOnMakeCheckBox.isSelected());
143 modifiableArtifact.setOutputPath(getConfiguredOutputPath());
144 myPropertiesEditors.applyProperties();
145 myLayoutTreeComponent.saveElementProperties();
148 @Nullable
149 private String getConfiguredOutputPath() {
150 String outputPath = FileUtil.toSystemIndependentName(myOutputDirectoryField.getText().trim());
151 if (outputPath.length() == 0) {
152 outputPath = null;
154 return outputPath;
157 public SourceItemsTree getSourceItemsTree() {
158 return mySourceItemsTree;
161 public void addListener(@NotNull final ArtifactEditorListener listener) {
162 myDispatcher.addListener(listener);
165 public ArtifactEditorContextImpl getContext() {
166 return myContext;
169 public void removeListener(@NotNull final ArtifactEditorListener listener) {
170 myDispatcher.removeListener(listener);
173 public Artifact getArtifact() {
174 return myContext.getArtifactModel().getArtifactByOriginal(myOriginalArtifact);
177 public CompositePackagingElement<?> getRootElement() {
178 return myLayoutTreeComponent.getRootElement();
181 public void rebuildTries() {
182 myLayoutTreeComponent.rebuildTree();
183 mySourceItemsTree.rebuildTree();
186 public void queueValidation() {
187 myContext.queueValidation();
190 public JComponent createMainComponent() {
191 mySourceItemsTree.initTree();
192 myLayoutTreeComponent.initTree();
193 myMainPanel.putClientProperty(DataManager.CLIENT_PROPERTY_DATA_PROVIDER, new TypeSafeDataProviderAdapter(new MyDataProvider()));
195 myErrorPanelPlace.add(myValidationManager.getMainErrorPanel(), BorderLayout.CENTER);
197 Splitter splitter = new Splitter(false);
198 final JPanel leftPanel = new JPanel(new BorderLayout());
199 leftPanel.add(myLayoutTreeComponent.getTreePanel(), BorderLayout.CENTER);
200 final Border border = BorderFactory.createEmptyBorder(3, 3, 3, 3);
201 leftPanel.setBorder(border);
202 splitter.setFirstComponent(leftPanel);
204 final JPanel rightPanel = new JPanel(new BorderLayout());
205 final JPanel rightTopPanel = new JPanel(new BorderLayout());
206 final JPanel labelPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
207 labelPanel.add(new JLabel("Available Elements"));
208 final HyperlinkLabel link = new HyperlinkLabel("");
209 link.setIcon(IconLoader.getIcon("/general/help.png"));
210 link.addHyperlinkListener(new HyperlinkListener() {
211 public void hyperlinkUpdate(HyperlinkEvent e) {
212 if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
213 final JLabel label = new JLabel(ProjectBundle.message("artifact.source.items.tree.tooltip"));
214 label.setBorder(HintUtil.createHintBorder());
215 label.setBackground(HintUtil.INFORMATION_COLOR);
216 label.setOpaque(true);
217 HintManager.getInstance().showHint(label, RelativePoint.getSouthEastOf(link), HintManager.HIDE_BY_ANY_KEY | HintManager.HIDE_BY_TEXT_CHANGE, -1);
221 labelPanel.add(link);
222 rightTopPanel.add(labelPanel, BorderLayout.SOUTH);
223 rightPanel.add(rightTopPanel, BorderLayout.NORTH);
224 rightPanel.add(ScrollPaneFactory.createScrollPane(mySourceItemsTree), BorderLayout.CENTER);
225 rightPanel.setBorder(border);
226 splitter.setSecondComponent(rightPanel);
229 myShowContentCheckBox.addActionListener(new ActionListener() {
230 public void actionPerformed(ActionEvent e) {
231 final ThreeStateCheckBox.State state = myShowContentCheckBox.getState();
232 if (state == ThreeStateCheckBox.State.SELECTED) {
233 mySubstitutionParameters.setSubstituteAll();
235 else if (state == ThreeStateCheckBox.State.NOT_SELECTED) {
236 mySubstitutionParameters.setSubstituteNone();
238 myShowContentCheckBox.setThirdStateEnabled(false);
239 myLayoutTreeComponent.rebuildTree();
240 onShowContentSettingsChanged();
244 ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, createToolbarActionGroup(), true);
245 leftPanel.add(toolbar.getComponent(), BorderLayout.NORTH);
246 rightTopPanel.setPreferredSize(new Dimension(-1, toolbar.getComponent().getPreferredSize().height));
248 myTabbedPane = new TabbedPaneWrapper(this);
249 myTabbedPane.addTab("Output Layout", splitter);
250 myPropertiesEditors.addTabs(myTabbedPane);
251 myEditorPanel.add(myTabbedPane.getComponent(), BorderLayout.CENTER);
253 final LayoutTree tree = myLayoutTreeComponent.getLayoutTree();
254 new ShowAddPackagingElementPopupAction(this).registerCustomShortcutSet(CommonShortcuts.getNew(), tree);
255 PopupHandler.installPopupHandler(tree, createPopupActionGroup(), ActionPlaces.UNKNOWN, ActionManager.getInstance());
256 TreeToolTipHandler.install(tree);
257 ToolTipManager.sharedInstance().registerComponent(tree);
258 rebuildTries();
259 return getMainComponent();
262 private void onShowContentSettingsChanged() {
263 myContext.getParent().getDefaultSettings().setTypesToShowContent(mySubstitutionParameters.getTypesToSubstitute());
266 public void updateShowContentCheckbox() {
267 final ThreeStateCheckBox.State state;
268 if (mySubstitutionParameters.isAllSubstituted()) {
269 state = ThreeStateCheckBox.State.SELECTED;
271 else if (mySubstitutionParameters.isNoneSubstituted()) {
272 state = ThreeStateCheckBox.State.NOT_SELECTED;
274 else {
275 state = ThreeStateCheckBox.State.DONT_CARE;
277 myShowContentCheckBox.setThirdStateEnabled(state == ThreeStateCheckBox.State.DONT_CARE);
278 myShowContentCheckBox.setState(state);
279 onShowContentSettingsChanged();
282 public ArtifactEditorSettings createSettings() {
283 return new ArtifactEditorSettings(myLayoutTreeComponent.isSortElements(), mySubstitutionParameters.getTypesToSubstitute());
286 private DefaultActionGroup createToolbarActionGroup() {
287 final DefaultActionGroup toolbarActionGroup = new DefaultActionGroup();
289 final List<AnAction> createActions = new ArrayList<AnAction>(createNewElementActions());
290 for (AnAction createAction : createActions) {
291 toolbarActionGroup.add(createAction);
294 toolbarActionGroup.add(new RemovePackagingElementAction(this));
295 toolbarActionGroup.add(Separator.getInstance());
296 toolbarActionGroup.add(new SortElementsToggleAction(this.getLayoutTreeComponent()));
297 toolbarActionGroup.add(new MovePackagingElementAction(myLayoutTreeComponent, "Move Up", "", IconLoader.getIcon("/actions/moveUp.png"), -1));
298 toolbarActionGroup.add(new MovePackagingElementAction(myLayoutTreeComponent, "Move Down", "", IconLoader.getIcon("/actions/moveDown.png"), 1));
299 return toolbarActionGroup;
302 public List<AnAction> createNewElementActions() {
303 final List<AnAction> createActions = new ArrayList<AnAction>();
304 AddCompositeElementAction.addCompositeCreateActions(createActions, this);
305 createActions.add(createAddNonCompositeElementGroup());
306 return createActions;
309 private DefaultActionGroup createPopupActionGroup() {
310 final LayoutTree tree = myLayoutTreeComponent.getLayoutTree();
312 DefaultActionGroup popupActionGroup = new DefaultActionGroup();
313 final List<AnAction> createActions = new ArrayList<AnAction>();
314 AddCompositeElementAction.addCompositeCreateActions(createActions, this);
315 for (AnAction createAction : createActions) {
316 popupActionGroup.add(createAction);
318 popupActionGroup.add(createAddNonCompositeElementGroup());
319 final RemovePackagingElementAction removeAction = new RemovePackagingElementAction(this);
320 removeAction.registerCustomShortcutSet(CommonShortcuts.DELETE, tree);
321 popupActionGroup.add(removeAction);
322 popupActionGroup.add(new ExtractArtifactAction(this));
323 popupActionGroup.add(new InlineArtifactAction(this));
324 popupActionGroup.add(new RenamePackagingElementAction(this));
325 popupActionGroup.add(new SurroundElementWithAction(this));
326 popupActionGroup.add(Separator.getInstance());
327 popupActionGroup.add(new HideContentAction(this));
328 popupActionGroup.add(new LayoutTreeNavigateAction(myLayoutTreeComponent));
329 popupActionGroup.add(new LayoutTreeFindUsagesAction(myLayoutTreeComponent, myProject, myContext.getParent()));
331 popupActionGroup.add(Separator.getInstance());
332 CommonActionsManager actionsManager = CommonActionsManager.getInstance();
333 DefaultTreeExpander treeExpander = new DefaultTreeExpander(tree);
334 popupActionGroup.add(actionsManager.createExpandAllAction(treeExpander, tree));
335 popupActionGroup.add(actionsManager.createCollapseAllAction(treeExpander, tree));
336 return popupActionGroup;
339 public ComplexElementSubstitutionParameters getSubstitutionParameters() {
340 return mySubstitutionParameters;
343 private ActionGroup createAddNonCompositeElementGroup() {
344 DefaultActionGroup group = new DefaultActionGroup(ProjectBundle.message("artifacts.add.copy.action"), true);
345 group.getTemplatePresentation().setIcon(Icons.ADD_ICON);
346 for (PackagingElementType<?> type : PackagingElementFactory.getInstance().getNonCompositeElementTypes()) {
347 group.add(new AddNewPackagingElementAction(type, this));
349 return group;
352 public JComponent getMainComponent() {
353 return myMainPanel;
356 public void addNewPackagingElement(@NotNull PackagingElementType<?> type) {
357 myLayoutTreeComponent.addNewPackagingElement(type);
358 mySourceItemsTree.rebuildTree();
361 public void removeSelectedElements() {
362 myLayoutTreeComponent.removeSelectedElements();
365 public boolean isModified() {
366 return myBuildOnMakeCheckBox.isSelected() != myOriginalArtifact.isBuildOnMake()
367 || !Comparing.equal(getConfiguredOutputPath(), myOriginalArtifact.getOutputPath())
368 || myPropertiesEditors.isModified()
369 || myLayoutTreeComponent.isPropertiesModified();
372 public void dispose() {
373 myDisposed = true;
376 public boolean isDisposed() {
377 return myDisposed;
380 public LayoutTreeComponent getLayoutTreeComponent() {
381 return myLayoutTreeComponent;
384 public void updateOutputPath(@NotNull String oldArtifactName, @NotNull final String newArtifactName) {
385 final String oldDefaultPath = ArtifactUtil.getDefaultArtifactOutputPath(oldArtifactName, myProject);
386 if (Comparing.equal(oldDefaultPath, getConfiguredOutputPath())) {
387 setOutputPath(ArtifactUtil.getDefaultArtifactOutputPath(newArtifactName, myProject));
388 final CompositePackagingElement<?> root = getRootElement();
389 if (root instanceof ArchivePackagingElement) {
390 String oldFileName = FileUtil.sanitizeFileName(oldArtifactName);
391 final String name = ((ArchivePackagingElement)root).getArchiveFileName();
392 final String fileName = FileUtil.getNameWithoutExtension(name);
393 final String extension = FileUtil.getExtension(name);
394 if (fileName.equals(oldFileName) && extension.length() > 0) {
395 myLayoutTreeComponent.editLayout(new Runnable() {
396 public void run() {
397 ((ArchivePackagingElement)getRootElement()).setArchiveFileName(FileUtil.sanitizeFileName(newArtifactName) + "." + extension);
400 myLayoutTreeComponent.updateRootNode();
406 public void updateLayoutTree() {
407 myLayoutTreeComponent.rebuildTree();
410 public void putLibraryIntoDefaultLocation(@NotNull Library library) {
411 myLayoutTreeComponent.putIntoDefaultLocations(Collections.singletonList(new LibrarySourceItem(library)));
414 public void putModuleIntoDefaultLocation(@NotNull Module module) {
415 myLayoutTreeComponent.putIntoDefaultLocations(Collections.singletonList(new ModuleOutputSourceItem(module)));
418 public void addToClasspath(final CompositePackagingElement<?> element, List<String> classpath) {
419 myLayoutTreeComponent.saveElementProperties();
420 ManifestFileConfiguration manifest = myContext.getManifestFile(element, getArtifact().getArtifactType());
421 if (manifest == null) {
422 final VirtualFile file = ManifestFileUtil.showDialogAndCreateManifest(myContext, element);
423 if (file == null) {
424 return;
427 ManifestFileUtil.addManifestFileToLayout(file.getPath(), myContext, element);
428 manifest = myContext.getManifestFile(element, getArtifact().getArtifactType());
431 if (manifest != null) {
432 manifest.addToClasspath(classpath);
434 myLayoutTreeComponent.resetElementProperties();
437 public void setArtifactType(ArtifactType artifactType) {
438 final ModifiableArtifact modifiableArtifact = myContext.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact);
439 modifiableArtifact.setArtifactType(artifactType);
441 myPropertiesEditors.removeTabs(myTabbedPane);
442 myPropertiesEditors = new ArtifactPropertiesEditors(myContext, myOriginalArtifact, getArtifact());
443 myPropertiesEditors.addTabs(myTabbedPane);
445 final CompositePackagingElement<?> oldRootElement = getRootElement();
446 final CompositePackagingElement<?> newRootElement = artifactType.createRootElement(getArtifact().getName());
447 ArtifactUtil.copyChildren(oldRootElement, newRootElement, myProject);
448 myLayoutTreeComponent.setRootElement(newRootElement);
451 public ArtifactValidationManagerImpl getValidationManager() {
452 return myValidationManager;
455 private class MyDataProvider implements TypeSafeDataProvider {
456 public void calcData(DataKey key, DataSink sink) {
457 if (ARTIFACTS_EDITOR_KEY.equals(key)) {
458 sink.put(ARTIFACTS_EDITOR_KEY, ArtifactEditorImpl.this);