IDEA-27190: Artifacts: Add context action to create .jar archieve from module output...
[fedora-idea.git] / java / idea-ui / src / com / intellij / openapi / roots / ui / configuration / artifacts / ArtifactEditorImpl.java
blob14b084792a09494c9697b8ac1c9dd4d5b356696a
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.ide.CommonActionsManager;
19 import com.intellij.ide.DataManager;
20 import com.intellij.ide.DefaultTreeExpander;
21 import com.intellij.ide.impl.TypeSafeDataProviderAdapter;
22 import com.intellij.openapi.actionSystem.*;
23 import com.intellij.openapi.compiler.CompilerBundle;
24 import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
25 import com.intellij.openapi.module.Module;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.project.ProjectBundle;
28 import com.intellij.openapi.roots.libraries.Library;
29 import com.intellij.openapi.roots.ui.configuration.artifacts.actions.*;
30 import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.LibrarySourceItem;
31 import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.ModuleOutputSourceItem;
32 import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemsTree;
33 import com.intellij.openapi.ui.FixedSizeButton;
34 import com.intellij.openapi.ui.Splitter;
35 import com.intellij.openapi.ui.TextFieldWithBrowseButton;
36 import com.intellij.openapi.util.Comparing;
37 import com.intellij.openapi.util.Disposer;
38 import com.intellij.openapi.util.IconLoader;
39 import com.intellij.openapi.util.io.FileUtil;
40 import com.intellij.openapi.vfs.VirtualFile;
41 import com.intellij.packaging.artifacts.Artifact;
42 import com.intellij.packaging.artifacts.ArtifactType;
43 import com.intellij.packaging.artifacts.ModifiableArtifact;
44 import com.intellij.packaging.elements.ComplexPackagingElementType;
45 import com.intellij.packaging.elements.CompositePackagingElement;
46 import com.intellij.packaging.elements.PackagingElementFactory;
47 import com.intellij.packaging.elements.PackagingElementType;
48 import com.intellij.packaging.impl.artifacts.ArtifactUtil;
49 import com.intellij.packaging.impl.elements.ArchivePackagingElement;
50 import com.intellij.packaging.impl.elements.ManifestFileUtil;
51 import com.intellij.packaging.ui.ManifestFileConfiguration;
52 import com.intellij.ui.PopupHandler;
53 import com.intellij.ui.ScrollPaneFactory;
54 import com.intellij.ui.TabbedPaneWrapper;
55 import com.intellij.ui.TreeToolTipHandler;
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 java.awt.*;
65 import java.awt.event.ActionEvent;
66 import java.awt.event.ActionListener;
67 import java.util.ArrayList;
68 import java.util.Collections;
69 import java.util.List;
71 /**
72 * @author nik
74 public class ArtifactEditorImpl implements ArtifactEditorEx {
75 private JPanel myMainPanel;
76 private JCheckBox myBuildOnMakeCheckBox;
77 private TextFieldWithBrowseButton myOutputDirectoryField;
78 private JPanel myEditorPanel;
79 private JPanel myErrorPanelPlace;
80 private ThreeStateCheckBox myShowContentCheckBox;
81 private FixedSizeButton myShowSpecificContentOptionsButton;
82 private ActionGroup myShowSpecificContentOptionsGroup;
83 private final Project myProject;
84 private final ComplexElementSubstitutionParameters mySubstitutionParameters = new ComplexElementSubstitutionParameters();
85 private final EventDispatcher<ArtifactEditorListener> myDispatcher = EventDispatcher.create(ArtifactEditorListener.class);
86 private final ArtifactEditorContextImpl myContext;
87 private SourceItemsTree mySourceItemsTree;
88 private final Artifact myOriginalArtifact;
89 private final LayoutTreeComponent myLayoutTreeComponent;
90 private TabbedPaneWrapper myTabbedPane;
91 private ArtifactPropertiesEditors myPropertiesEditors;
92 private ArtifactValidationManagerImpl myValidationManager;
93 private boolean myDisposed;
95 public ArtifactEditorImpl(final @NotNull ArtifactsStructureConfigurableContext context, @NotNull Artifact artifact, @NotNull ArtifactEditorSettings settings) {
96 myContext = createArtifactEditorContext(context);
97 myOriginalArtifact = artifact;
98 myProject = context.getProject();
99 mySubstitutionParameters.setTypesToShowContent(settings.getTypesToShowContent());
100 mySourceItemsTree = new SourceItemsTree(myContext, this);
101 myLayoutTreeComponent = new LayoutTreeComponent(this, mySubstitutionParameters, myContext, myOriginalArtifact, settings.isSortElements());
102 myPropertiesEditors = new ArtifactPropertiesEditors(myContext, myOriginalArtifact, myOriginalArtifact);
103 Disposer.register(this, mySourceItemsTree);
104 Disposer.register(this, myLayoutTreeComponent);
105 myBuildOnMakeCheckBox.setSelected(artifact.isBuildOnMake());
106 final String outputPath = artifact.getOutputPath();
107 myOutputDirectoryField.addBrowseFolderListener(CompilerBundle.message("dialog.title.output.directory.for.artifact"),
108 CompilerBundle.message("chooser.description.select.output.directory.for.0.artifact",
109 getArtifact().getName()), myProject,
110 FileChooserDescriptorFactory.createSingleFolderDescriptor());
111 myShowSpecificContentOptionsGroup = createShowSpecificContentOptionsGroup();
112 myShowSpecificContentOptionsButton.addActionListener(new ActionListener() {
113 public void actionPerformed(ActionEvent e) {
114 ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, myShowSpecificContentOptionsGroup).getComponent().show(myShowSpecificContentOptionsButton, 0, 0);
117 setOutputPath(outputPath);
118 myValidationManager = new ArtifactValidationManagerImpl(this);
119 updateShowContentCheckbox();
122 protected ArtifactEditorContextImpl createArtifactEditorContext(ArtifactsStructureConfigurableContext parentContext) {
123 return new ArtifactEditorContextImpl(parentContext, this);
126 private ActionGroup createShowSpecificContentOptionsGroup() {
127 final DefaultActionGroup group = new DefaultActionGroup();
128 for (ComplexPackagingElementType<?> type : PackagingElementFactory.getInstance().getComplexElementTypes()) {
129 group.add(new ToggleShowElementContentAction(type, this));
131 return group;
134 private void setOutputPath(@Nullable String outputPath) {
135 myOutputDirectoryField.setText(outputPath != null ? FileUtil.toSystemDependentName(outputPath) : null);
138 public void apply() {
139 final ModifiableArtifact modifiableArtifact = myContext.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact);
140 modifiableArtifact.setBuildOnMake(myBuildOnMakeCheckBox.isSelected());
141 modifiableArtifact.setOutputPath(getConfiguredOutputPath());
142 myPropertiesEditors.applyProperties();
143 myLayoutTreeComponent.saveElementProperties();
146 @Nullable
147 private String getConfiguredOutputPath() {
148 String outputPath = FileUtil.toSystemIndependentName(myOutputDirectoryField.getText().trim());
149 if (outputPath.length() == 0) {
150 outputPath = null;
152 return outputPath;
155 public SourceItemsTree getSourceItemsTree() {
156 return mySourceItemsTree;
159 public void addListener(@NotNull final ArtifactEditorListener listener) {
160 myDispatcher.addListener(listener);
163 public ArtifactEditorContextImpl getContext() {
164 return myContext;
167 public void removeListener(@NotNull final ArtifactEditorListener listener) {
168 myDispatcher.removeListener(listener);
171 public Artifact getArtifact() {
172 return myContext.getArtifactModel().getArtifactByOriginal(myOriginalArtifact);
175 public CompositePackagingElement<?> getRootElement() {
176 return myLayoutTreeComponent.getRootElement();
179 public void rebuildTries() {
180 myLayoutTreeComponent.rebuildTree();
181 mySourceItemsTree.rebuildTree();
184 public void queueValidation() {
185 myContext.queueValidation();
188 public JComponent createMainComponent() {
189 mySourceItemsTree.initTree();
190 myLayoutTreeComponent.initTree();
191 myMainPanel.putClientProperty(DataManager.CLIENT_PROPERTY_DATA_PROVIDER, new TypeSafeDataProviderAdapter(new MyDataProvider()));
193 myErrorPanelPlace.add(myValidationManager.getMainErrorPanel(), BorderLayout.CENTER);
195 Splitter splitter = new Splitter(false);
196 final JPanel leftPanel = new JPanel(new BorderLayout());
197 leftPanel.add(myLayoutTreeComponent.getTreePanel(), BorderLayout.CENTER);
198 final Border border = BorderFactory.createEmptyBorder(3, 3, 3, 3);
199 leftPanel.setBorder(border);
200 splitter.setFirstComponent(leftPanel);
202 final JPanel rightPanel = new JPanel(new BorderLayout());
203 final JPanel rightTopPanel = new JPanel(new BorderLayout());
204 rightTopPanel.add(new JLabel("Available Elements (drag'n'drop to layout tree)"), BorderLayout.SOUTH);
205 rightPanel.add(rightTopPanel, BorderLayout.NORTH);
206 rightPanel.add(ScrollPaneFactory.createScrollPane(mySourceItemsTree.getTree()), BorderLayout.CENTER);
207 rightPanel.setBorder(border);
208 splitter.setSecondComponent(rightPanel);
211 myShowContentCheckBox.addActionListener(new ActionListener() {
212 public void actionPerformed(ActionEvent e) {
213 final ThreeStateCheckBox.State state = myShowContentCheckBox.getState();
214 if (state == ThreeStateCheckBox.State.SELECTED) {
215 mySubstitutionParameters.setSubstituteAll();
217 else if (state == ThreeStateCheckBox.State.NOT_SELECTED) {
218 mySubstitutionParameters.setSubstituteNone();
220 myShowContentCheckBox.setThirdStateEnabled(false);
221 myLayoutTreeComponent.rebuildTree();
222 onShowContentSettingsChanged();
226 ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, createToolbarActionGroup(), true);
227 leftPanel.add(toolbar.getComponent(), BorderLayout.NORTH);
228 rightTopPanel.setPreferredSize(new Dimension(-1, toolbar.getComponent().getPreferredSize().height));
230 myTabbedPane = new TabbedPaneWrapper(this);
231 myTabbedPane.addTab("Output Layout", splitter);
232 myPropertiesEditors.addTabs(myTabbedPane);
233 myEditorPanel.add(myTabbedPane.getComponent(), BorderLayout.CENTER);
235 final LayoutTree tree = myLayoutTreeComponent.getLayoutTree();
236 new ShowAddPackagingElementPopupAction(this).registerCustomShortcutSet(CommonShortcuts.getNew(), tree);
237 PopupHandler.installPopupHandler(tree, createPopupActionGroup(), ActionPlaces.UNKNOWN, ActionManager.getInstance());
238 TreeToolTipHandler.install(tree);
239 ToolTipManager.sharedInstance().registerComponent(tree);
240 rebuildTries();
241 return getMainComponent();
244 private void onShowContentSettingsChanged() {
245 myContext.getParent().getDefaultSettings().setTypesToShowContent(mySubstitutionParameters.getTypesToSubstitute());
248 public void updateShowContentCheckbox() {
249 final ThreeStateCheckBox.State state;
250 if (mySubstitutionParameters.isAllSubstituted()) {
251 state = ThreeStateCheckBox.State.SELECTED;
253 else if (mySubstitutionParameters.isNoneSubstituted()) {
254 state = ThreeStateCheckBox.State.NOT_SELECTED;
256 else {
257 state = ThreeStateCheckBox.State.DONT_CARE;
259 myShowContentCheckBox.setThirdStateEnabled(state == ThreeStateCheckBox.State.DONT_CARE);
260 myShowContentCheckBox.setState(state);
261 onShowContentSettingsChanged();
264 public ArtifactEditorSettings createSettings() {
265 return new ArtifactEditorSettings(myLayoutTreeComponent.isSortElements(), mySubstitutionParameters.getTypesToSubstitute());
268 private DefaultActionGroup createToolbarActionGroup() {
269 final DefaultActionGroup toolbarActionGroup = new DefaultActionGroup();
271 final List<AnAction> createActions = new ArrayList<AnAction>(createNewElementActions());
272 for (AnAction createAction : createActions) {
273 toolbarActionGroup.add(createAction);
276 toolbarActionGroup.add(new RemovePackagingElementAction(this));
277 toolbarActionGroup.add(Separator.getInstance());
278 toolbarActionGroup.add(new SortElementsToggleAction(this.getLayoutTreeComponent()));
279 toolbarActionGroup.add(new MovePackagingElementAction(myLayoutTreeComponent, "Move Up", "", IconLoader.getIcon("/actions/moveUp.png"), -1));
280 toolbarActionGroup.add(new MovePackagingElementAction(myLayoutTreeComponent, "Move Down", "", IconLoader.getIcon("/actions/moveDown.png"), 1));
281 return toolbarActionGroup;
284 public List<AnAction> createNewElementActions() {
285 final List<AnAction> createActions = new ArrayList<AnAction>();
286 AddCompositeElementAction.addCompositeCreateActions(createActions, this);
287 createActions.add(createAddNonCompositeElementGroup());
288 return createActions;
291 private DefaultActionGroup createPopupActionGroup() {
292 final LayoutTree tree = myLayoutTreeComponent.getLayoutTree();
294 DefaultActionGroup popupActionGroup = new DefaultActionGroup();
295 final List<AnAction> createActions = new ArrayList<AnAction>();
296 AddCompositeElementAction.addCompositeCreateActions(createActions, this);
297 for (AnAction createAction : createActions) {
298 popupActionGroup.add(createAction);
300 popupActionGroup.add(createAddNonCompositeElementGroup());
301 final RemovePackagingElementAction removeAction = new RemovePackagingElementAction(this);
302 removeAction.registerCustomShortcutSet(CommonShortcuts.DELETE, tree);
303 popupActionGroup.add(removeAction);
304 popupActionGroup.add(new ExtractArtifactAction(this));
305 popupActionGroup.add(new InlineArtifactAction(this));
306 popupActionGroup.add(new RenamePackagingElementAction(this));
307 popupActionGroup.add(new SurroundElementWithAction(this));
308 popupActionGroup.add(Separator.getInstance());
309 popupActionGroup.add(new HideContentAction(this));
310 popupActionGroup.add(new LayoutTreeNavigateAction(myLayoutTreeComponent));
311 popupActionGroup.add(new LayoutTreeFindUsagesAction(myLayoutTreeComponent, myProject, myContext.getParent()));
313 popupActionGroup.add(Separator.getInstance());
314 CommonActionsManager actionsManager = CommonActionsManager.getInstance();
315 DefaultTreeExpander treeExpander = new DefaultTreeExpander(tree);
316 popupActionGroup.add(actionsManager.createExpandAllAction(treeExpander, tree));
317 popupActionGroup.add(actionsManager.createCollapseAllAction(treeExpander, tree));
318 return popupActionGroup;
321 public ComplexElementSubstitutionParameters getSubstitutionParameters() {
322 return mySubstitutionParameters;
325 private ActionGroup createAddNonCompositeElementGroup() {
326 DefaultActionGroup group = new DefaultActionGroup(ProjectBundle.message("artifacts.add.copy.action"), true);
327 group.getTemplatePresentation().setIcon(Icons.ADD_ICON);
328 for (PackagingElementType<?> type : PackagingElementFactory.getInstance().getNonCompositeElementTypes()) {
329 group.add(new AddNewPackagingElementAction(type, this));
331 return group;
334 public JComponent getMainComponent() {
335 return myMainPanel;
338 public void addNewPackagingElement(@NotNull PackagingElementType<?> type) {
339 myLayoutTreeComponent.addNewPackagingElement(type);
340 mySourceItemsTree.rebuildTree();
343 public void removeSelectedElements() {
344 myLayoutTreeComponent.removeSelectedElements();
347 public boolean isModified() {
348 return myBuildOnMakeCheckBox.isSelected() != myOriginalArtifact.isBuildOnMake()
349 || !Comparing.equal(getConfiguredOutputPath(), myOriginalArtifact.getOutputPath())
350 || myPropertiesEditors.isModified()
351 || myLayoutTreeComponent.isPropertiesModified();
354 public void dispose() {
355 myDisposed = true;
358 public boolean isDisposed() {
359 return myDisposed;
362 public LayoutTreeComponent getLayoutTreeComponent() {
363 return myLayoutTreeComponent;
366 public void updateOutputPath(@NotNull String oldArtifactName, @NotNull final String newArtifactName) {
367 final String oldDefaultPath = ArtifactUtil.getDefaultArtifactOutputPath(oldArtifactName, myProject);
368 if (Comparing.equal(oldDefaultPath, getConfiguredOutputPath())) {
369 setOutputPath(ArtifactUtil.getDefaultArtifactOutputPath(newArtifactName, myProject));
370 final CompositePackagingElement<?> root = getRootElement();
371 if (root instanceof ArchivePackagingElement) {
372 String oldFileName = FileUtil.sanitizeFileName(oldArtifactName);
373 final String name = ((ArchivePackagingElement)root).getArchiveFileName();
374 final String fileName = FileUtil.getNameWithoutExtension(name);
375 final String extension = FileUtil.getExtension(name);
376 if (fileName.equals(oldFileName) && extension.length() > 0) {
377 myLayoutTreeComponent.editLayout(new Runnable() {
378 public void run() {
379 ((ArchivePackagingElement)getRootElement()).setArchiveFileName(FileUtil.sanitizeFileName(newArtifactName) + "." + extension);
382 myLayoutTreeComponent.updateRootNode();
388 public void updateLayoutTree() {
389 myLayoutTreeComponent.rebuildTree();
392 public void putLibraryIntoDefaultLocation(@NotNull Library library) {
393 myLayoutTreeComponent.putIntoDefaultLocations(Collections.singletonList(new LibrarySourceItem(library)));
396 public void putModuleIntoDefaultLocation(@NotNull Module module) {
397 myLayoutTreeComponent.putIntoDefaultLocations(Collections.singletonList(new ModuleOutputSourceItem(module)));
400 public void addToClasspath(final CompositePackagingElement<?> element, List<String> classpath) {
401 myLayoutTreeComponent.saveElementProperties();
402 ManifestFileConfiguration manifest = myContext.getManifestFile(element, getArtifact().getArtifactType());
403 if (manifest == null) {
404 final VirtualFile file = ManifestFileUtil.showDialogAndCreateManifest(myContext, element);
405 if (file == null) {
406 return;
409 ManifestFileUtil.addManifestFileToLayout(file.getPath(), myContext, element);
410 manifest = myContext.getManifestFile(element, getArtifact().getArtifactType());
413 if (manifest != null) {
414 manifest.addToClasspath(classpath);
416 myLayoutTreeComponent.resetElementProperties();
419 public void setArtifactType(ArtifactType artifactType) {
420 final ModifiableArtifact modifiableArtifact = myContext.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact);
421 modifiableArtifact.setArtifactType(artifactType);
423 myPropertiesEditors.removeTabs(myTabbedPane);
424 myPropertiesEditors = new ArtifactPropertiesEditors(myContext, myOriginalArtifact, getArtifact());
425 myPropertiesEditors.addTabs(myTabbedPane);
427 final CompositePackagingElement<?> oldRootElement = getRootElement();
428 final CompositePackagingElement<?> newRootElement = artifactType.createRootElement(getArtifact().getName());
429 ArtifactUtil.copyChildren(oldRootElement, newRootElement, myProject);
430 myLayoutTreeComponent.setRootElement(newRootElement);
433 public ArtifactValidationManagerImpl getValidationManager() {
434 return myValidationManager;
437 private class MyDataProvider implements TypeSafeDataProvider {
438 public void calcData(DataKey key, DataSink sink) {
439 if (ARTIFACTS_EDITOR_KEY.equals(key)) {
440 sink.put(ARTIFACTS_EDITOR_KEY, ArtifactEditorImpl.this);