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
;
63 import javax
.swing
.border
.Border
;
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
;
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));
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();
147 private String
getConfiguredOutputPath() {
148 String outputPath
= FileUtil
.toSystemIndependentName(myOutputDirectoryField
.getText().trim());
149 if (outputPath
.length() == 0) {
155 public SourceItemsTree
getSourceItemsTree() {
156 return mySourceItemsTree
;
159 public void addListener(@NotNull final ArtifactEditorListener listener
) {
160 myDispatcher
.addListener(listener
);
163 public ArtifactEditorContextImpl
getContext() {
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
);
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
;
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));
334 public JComponent
getMainComponent() {
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() {
358 public boolean isDisposed() {
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() {
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
);
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);