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
;
94 public ArtifactEditorImpl(final @NotNull ArtifactsStructureConfigurableContext context
, @NotNull Artifact artifact
, @NotNull ArtifactEditorSettings settings
) {
95 myContext
= new ArtifactEditorContextImpl(context
, this);
96 myOriginalArtifact
= artifact
;
97 myProject
= context
.getProject();
98 mySubstitutionParameters
.setTypesToShowContent(settings
.getTypesToShowContent());
99 mySourceItemsTree
= new SourceItemsTree(myContext
, this);
100 myLayoutTreeComponent
= new LayoutTreeComponent(this, mySubstitutionParameters
, myContext
, myOriginalArtifact
, settings
.isSortElements());
101 myPropertiesEditors
= new ArtifactPropertiesEditors(myContext
, myOriginalArtifact
, myOriginalArtifact
);
102 Disposer
.register(this, mySourceItemsTree
);
103 Disposer
.register(this, myLayoutTreeComponent
);
104 myBuildOnMakeCheckBox
.setSelected(artifact
.isBuildOnMake());
105 final String outputPath
= artifact
.getOutputPath();
106 myOutputDirectoryField
.addBrowseFolderListener(CompilerBundle
.message("dialog.title.output.directory.for.artifact"),
107 CompilerBundle
.message("chooser.description.select.output.directory.for.0.artifact",
108 getArtifact().getName()), myProject
,
109 FileChooserDescriptorFactory
.createSingleFolderDescriptor());
110 myShowSpecificContentOptionsGroup
= createShowSpecificContentOptionsGroup();
111 myShowSpecificContentOptionsButton
.addActionListener(new ActionListener() {
112 public void actionPerformed(ActionEvent e
) {
113 ActionManager
.getInstance().createActionPopupMenu(ActionPlaces
.UNKNOWN
, myShowSpecificContentOptionsGroup
).getComponent().show(myShowSpecificContentOptionsButton
, 0, 0);
116 setOutputPath(outputPath
);
117 myValidationManager
= new ArtifactValidationManagerImpl(this);
118 updateShowContentCheckbox();
121 private ActionGroup
createShowSpecificContentOptionsGroup() {
122 final DefaultActionGroup group
= new DefaultActionGroup();
123 for (ComplexPackagingElementType
<?
> type
: PackagingElementFactory
.getInstance().getComplexElementTypes()) {
124 group
.add(new ToggleShowElementContentAction(type
, this));
129 private void setOutputPath(@Nullable String outputPath
) {
130 myOutputDirectoryField
.setText(outputPath
!= null ? FileUtil
.toSystemDependentName(outputPath
) : null);
133 public void apply() {
134 final ModifiableArtifact modifiableArtifact
= myContext
.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact
);
135 modifiableArtifact
.setBuildOnMake(myBuildOnMakeCheckBox
.isSelected());
136 modifiableArtifact
.setOutputPath(getConfiguredOutputPath());
137 myPropertiesEditors
.applyProperties();
138 myLayoutTreeComponent
.saveElementProperties();
142 private String
getConfiguredOutputPath() {
143 String outputPath
= FileUtil
.toSystemIndependentName(myOutputDirectoryField
.getText().trim());
144 if (outputPath
.length() == 0) {
150 public SourceItemsTree
getSourceItemsTree() {
151 return mySourceItemsTree
;
154 public void addListener(@NotNull final ArtifactEditorListener listener
) {
155 myDispatcher
.addListener(listener
);
158 public ArtifactEditorContextImpl
getContext() {
162 public void removeListener(@NotNull final ArtifactEditorListener listener
) {
163 myDispatcher
.removeListener(listener
);
166 public Artifact
getArtifact() {
167 return myContext
.getArtifactModel().getArtifactByOriginal(myOriginalArtifact
);
170 public CompositePackagingElement
<?
> getRootElement() {
171 return myLayoutTreeComponent
.getRootElement();
174 public void rebuildTries() {
175 myLayoutTreeComponent
.rebuildTree();
176 mySourceItemsTree
.rebuildTree();
179 public void queueValidation() {
180 myContext
.queueValidation();
183 public JComponent
createMainComponent() {
184 mySourceItemsTree
.initTree();
185 myLayoutTreeComponent
.initTree();
186 myMainPanel
.putClientProperty(DataManager
.CLIENT_PROPERTY_DATA_PROVIDER
, new TypeSafeDataProviderAdapter(new MyDataProvider()));
188 myErrorPanelPlace
.add(myValidationManager
.getMainErrorPanel(), BorderLayout
.CENTER
);
190 Splitter splitter
= new Splitter(false);
191 final JPanel leftPanel
= new JPanel(new BorderLayout());
192 leftPanel
.add(myLayoutTreeComponent
.getTreePanel(), BorderLayout
.CENTER
);
193 final Border border
= BorderFactory
.createEmptyBorder(3, 3, 3, 3);
194 leftPanel
.setBorder(border
);
195 splitter
.setFirstComponent(leftPanel
);
197 final JPanel rightPanel
= new JPanel(new BorderLayout());
198 final JPanel rightTopPanel
= new JPanel(new BorderLayout());
199 rightTopPanel
.add(new JLabel("Available Elements (drag'n'drop to layout tree)"), BorderLayout
.SOUTH
);
200 rightPanel
.add(rightTopPanel
, BorderLayout
.NORTH
);
201 rightPanel
.add(ScrollPaneFactory
.createScrollPane(mySourceItemsTree
.getTree()), BorderLayout
.CENTER
);
202 rightPanel
.setBorder(border
);
203 splitter
.setSecondComponent(rightPanel
);
206 myShowContentCheckBox
.addActionListener(new ActionListener() {
207 public void actionPerformed(ActionEvent e
) {
208 final ThreeStateCheckBox
.State state
= myShowContentCheckBox
.getState();
209 if (state
== ThreeStateCheckBox
.State
.SELECTED
) {
210 mySubstitutionParameters
.setSubstituteAll();
212 else if (state
== ThreeStateCheckBox
.State
.NOT_SELECTED
) {
213 mySubstitutionParameters
.setSubstituteNone();
215 myShowContentCheckBox
.setThirdStateEnabled(false);
216 myLayoutTreeComponent
.rebuildTree();
217 onShowContentSettingsChanged();
221 ActionToolbar toolbar
= ActionManager
.getInstance().createActionToolbar(ActionPlaces
.UNKNOWN
, createToolbarActionGroup(), true);
222 leftPanel
.add(toolbar
.getComponent(), BorderLayout
.NORTH
);
223 rightTopPanel
.setPreferredSize(new Dimension(-1, toolbar
.getComponent().getPreferredSize().height
));
225 myTabbedPane
= new TabbedPaneWrapper(this);
226 myTabbedPane
.addTab("Output Layout", splitter
);
227 myPropertiesEditors
.addTabs(myTabbedPane
);
228 myEditorPanel
.add(myTabbedPane
.getComponent(), BorderLayout
.CENTER
);
230 final LayoutTree tree
= myLayoutTreeComponent
.getLayoutTree();
231 new ShowAddPackagingElementPopupAction(this).registerCustomShortcutSet(CommonShortcuts
.getNew(), tree
);
232 PopupHandler
.installPopupHandler(tree
, createPopupActionGroup(), ActionPlaces
.UNKNOWN
, ActionManager
.getInstance());
233 TreeToolTipHandler
.install(tree
);
234 ToolTipManager
.sharedInstance().registerComponent(tree
);
236 return getMainComponent();
239 private void onShowContentSettingsChanged() {
240 ((ArtifactsStructureConfigurableContextImpl
)myContext
.getParent()).getDefaultSettings().setTypesToShowContent(mySubstitutionParameters
.getTypesToSubstitute());
243 public void updateShowContentCheckbox() {
244 final ThreeStateCheckBox
.State state
;
245 if (mySubstitutionParameters
.isAllSubstituted()) {
246 state
= ThreeStateCheckBox
.State
.SELECTED
;
248 else if (mySubstitutionParameters
.isNoneSubstituted()) {
249 state
= ThreeStateCheckBox
.State
.NOT_SELECTED
;
252 state
= ThreeStateCheckBox
.State
.DONT_CARE
;
254 myShowContentCheckBox
.setThirdStateEnabled(state
== ThreeStateCheckBox
.State
.DONT_CARE
);
255 myShowContentCheckBox
.setState(state
);
256 onShowContentSettingsChanged();
259 public ArtifactEditorSettings
createSettings() {
260 return new ArtifactEditorSettings(myLayoutTreeComponent
.isSortElements(), mySubstitutionParameters
.getTypesToSubstitute());
263 private DefaultActionGroup
createToolbarActionGroup() {
264 final DefaultActionGroup toolbarActionGroup
= new DefaultActionGroup();
266 final List
<AnAction
> createActions
= new ArrayList
<AnAction
>(createNewElementActions());
267 for (AnAction createAction
: createActions
) {
268 toolbarActionGroup
.add(createAction
);
271 toolbarActionGroup
.add(new RemovePackagingElementAction(this));
272 toolbarActionGroup
.add(Separator
.getInstance());
273 toolbarActionGroup
.add(new SortElementsToggleAction(this.getLayoutTreeComponent()));
274 toolbarActionGroup
.add(new MovePackagingElementAction(myLayoutTreeComponent
, "Move Up", "", IconLoader
.getIcon("/actions/moveUp.png"), -1));
275 toolbarActionGroup
.add(new MovePackagingElementAction(myLayoutTreeComponent
, "Move Down", "", IconLoader
.getIcon("/actions/moveDown.png"), 1));
276 return toolbarActionGroup
;
279 public List
<AnAction
> createNewElementActions() {
280 final List
<AnAction
> createActions
= new ArrayList
<AnAction
>();
281 AddCompositeElementAction
.addCompositeCreateActions(createActions
, this);
282 createActions
.add(createAddNonCompositeElementGroup());
283 return createActions
;
286 private DefaultActionGroup
createPopupActionGroup() {
287 final LayoutTree tree
= myLayoutTreeComponent
.getLayoutTree();
289 DefaultActionGroup popupActionGroup
= new DefaultActionGroup();
290 final List
<AnAction
> createActions
= new ArrayList
<AnAction
>();
291 AddCompositeElementAction
.addCompositeCreateActions(createActions
, this);
292 for (AnAction createAction
: createActions
) {
293 popupActionGroup
.add(createAction
);
295 popupActionGroup
.add(createAddNonCompositeElementGroup());
296 final RemovePackagingElementAction removeAction
= new RemovePackagingElementAction(this);
297 removeAction
.registerCustomShortcutSet(CommonShortcuts
.DELETE
, tree
);
298 popupActionGroup
.add(removeAction
);
299 popupActionGroup
.add(new ExtractArtifactAction(this));
300 popupActionGroup
.add(new InlineArtifactAction(this));
301 popupActionGroup
.add(new RenamePackagingElementAction(this));
302 popupActionGroup
.add(Separator
.getInstance());
303 popupActionGroup
.add(new HideContentAction(this));
304 popupActionGroup
.add(new LayoutTreeNavigateAction(myLayoutTreeComponent
));
305 popupActionGroup
.add(new LayoutTreeFindUsagesAction(myLayoutTreeComponent
, myProject
, myContext
.getParent()));
307 popupActionGroup
.add(Separator
.getInstance());
308 CommonActionsManager actionsManager
= CommonActionsManager
.getInstance();
309 DefaultTreeExpander treeExpander
= new DefaultTreeExpander(tree
);
310 popupActionGroup
.add(actionsManager
.createExpandAllAction(treeExpander
, tree
));
311 popupActionGroup
.add(actionsManager
.createCollapseAllAction(treeExpander
, tree
));
312 return popupActionGroup
;
315 public ComplexElementSubstitutionParameters
getSubstitutionParameters() {
316 return mySubstitutionParameters
;
319 private ActionGroup
createAddNonCompositeElementGroup() {
320 DefaultActionGroup group
= new DefaultActionGroup(ProjectBundle
.message("artifacts.add.copy.action"), true);
321 group
.getTemplatePresentation().setIcon(Icons
.ADD_ICON
);
322 for (PackagingElementType
<?
> type
: PackagingElementFactory
.getInstance().getNonCompositeElementTypes()) {
323 group
.add(new AddNewPackagingElementAction(type
, this));
328 public JComponent
getMainComponent() {
332 public void addNewPackagingElement(@NotNull PackagingElementType
<?
> type
) {
333 myLayoutTreeComponent
.addNewPackagingElement(type
);
334 mySourceItemsTree
.rebuildTree();
337 public void removeSelectedElements() {
338 myLayoutTreeComponent
.removeSelectedElements();
341 public boolean isModified() {
342 return myBuildOnMakeCheckBox
.isSelected() != myOriginalArtifact
.isBuildOnMake()
343 || !Comparing
.equal(getConfiguredOutputPath(), myOriginalArtifact
.getOutputPath())
344 || myPropertiesEditors
.isModified()
345 || myLayoutTreeComponent
.isPropertiesModified();
348 public void dispose() {
351 public LayoutTreeComponent
getLayoutTreeComponent() {
352 return myLayoutTreeComponent
;
355 public void updateOutputPath(@NotNull String oldArtifactName
, @NotNull final String newArtifactName
) {
356 final String oldDefaultPath
= ArtifactUtil
.getDefaultArtifactOutputPath(oldArtifactName
, myProject
);
357 if (Comparing
.equal(oldDefaultPath
, getConfiguredOutputPath())) {
358 setOutputPath(ArtifactUtil
.getDefaultArtifactOutputPath(newArtifactName
, myProject
));
359 final CompositePackagingElement
<?
> root
= getRootElement();
360 if (root
instanceof ArchivePackagingElement
) {
361 String oldFileName
= FileUtil
.sanitizeFileName(oldArtifactName
);
362 final String name
= ((ArchivePackagingElement
)root
).getArchiveFileName();
363 final String fileName
= FileUtil
.getNameWithoutExtension(name
);
364 final String extension
= FileUtil
.getExtension(name
);
365 if (fileName
.equals(oldFileName
) && extension
.length() > 0) {
366 myLayoutTreeComponent
.editLayout(new Runnable() {
368 ((ArchivePackagingElement
)getRootElement()).setArchiveFileName(FileUtil
.sanitizeFileName(newArtifactName
) + "." + extension
);
371 myLayoutTreeComponent
.updateRootNode();
377 public void updateLayoutTree() {
378 myLayoutTreeComponent
.rebuildTree();
381 public void putLibraryIntoDefaultLocation(@NotNull Library library
) {
382 myLayoutTreeComponent
.putIntoDefaultLocations(Collections
.singletonList(new LibrarySourceItem(library
)));
385 public void putModuleIntoDefaultLocation(@NotNull Module module
) {
386 myLayoutTreeComponent
.putIntoDefaultLocations(Collections
.singletonList(new ModuleOutputSourceItem(module
)));
389 public void addToClasspath(final CompositePackagingElement
<?
> element
, List
<String
> classpath
) {
390 myLayoutTreeComponent
.saveElementProperties();
391 ManifestFileConfiguration manifest
= myContext
.getManifestFile(element
, getArtifact().getArtifactType());
392 if (manifest
== null) {
393 final VirtualFile file
= ManifestFileUtil
.showDialogAndCreateManifest(myContext
, element
);
398 ManifestFileUtil
.addManifestFileToLayout(file
.getPath(), myContext
, element
);
399 manifest
= myContext
.getManifestFile(element
, getArtifact().getArtifactType());
402 if (manifest
!= null) {
403 manifest
.addToClasspath(classpath
);
405 myLayoutTreeComponent
.resetElementProperties();
408 public void setArtifactType(ArtifactType artifactType
) {
409 final ModifiableArtifact modifiableArtifact
= myContext
.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact
);
410 modifiableArtifact
.setArtifactType(artifactType
);
412 myPropertiesEditors
.removeTabs(myTabbedPane
);
413 myPropertiesEditors
= new ArtifactPropertiesEditors(myContext
, myOriginalArtifact
, getArtifact());
414 myPropertiesEditors
.addTabs(myTabbedPane
);
416 final CompositePackagingElement
<?
> oldRootElement
= getRootElement();
417 final CompositePackagingElement
<?
> newRootElement
= artifactType
.createRootElement(getArtifact().getName());
418 ArtifactUtil
.copyChildren(oldRootElement
, newRootElement
, myProject
);
419 myLayoutTreeComponent
.setRootElement(newRootElement
);
422 public ArtifactValidationManagerImpl
getValidationManager() {
423 return myValidationManager
;
426 private class MyDataProvider
implements TypeSafeDataProvider
{
427 public void calcData(DataKey key
, DataSink sink
) {
428 if (ARTIFACTS_EDITOR_KEY
.equals(key
)) {
429 sink
.put(ARTIFACTS_EDITOR_KEY
, ArtifactEditorImpl
.this);