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
.project
.Project
;
26 import com
.intellij
.openapi
.project
.ProjectBundle
;
27 import com
.intellij
.openapi
.roots
.libraries
.Library
;
28 import com
.intellij
.openapi
.roots
.ui
.configuration
.artifacts
.actions
.*;
29 import com
.intellij
.openapi
.roots
.ui
.configuration
.artifacts
.sourceItems
.LibrarySourceItem
;
30 import com
.intellij
.openapi
.roots
.ui
.configuration
.artifacts
.sourceItems
.SourceItemsTree
;
31 import com
.intellij
.openapi
.ui
.FixedSizeButton
;
32 import com
.intellij
.openapi
.ui
.Splitter
;
33 import com
.intellij
.openapi
.ui
.TextFieldWithBrowseButton
;
34 import com
.intellij
.openapi
.util
.Comparing
;
35 import com
.intellij
.openapi
.util
.Disposer
;
36 import com
.intellij
.openapi
.util
.IconLoader
;
37 import com
.intellij
.openapi
.util
.io
.FileUtil
;
38 import com
.intellij
.packaging
.artifacts
.Artifact
;
39 import com
.intellij
.packaging
.artifacts
.ModifiableArtifact
;
40 import com
.intellij
.packaging
.elements
.ComplexPackagingElementType
;
41 import com
.intellij
.packaging
.elements
.CompositePackagingElement
;
42 import com
.intellij
.packaging
.elements
.PackagingElementFactory
;
43 import com
.intellij
.packaging
.elements
.PackagingElementType
;
44 import com
.intellij
.packaging
.impl
.artifacts
.ArtifactUtil
;
45 import com
.intellij
.packaging
.impl
.elements
.ArchivePackagingElement
;
46 import com
.intellij
.packaging
.ui
.ManifestFileConfiguration
;
47 import com
.intellij
.ui
.PopupHandler
;
48 import com
.intellij
.ui
.ScrollPaneFactory
;
49 import com
.intellij
.ui
.TabbedPaneWrapper
;
50 import com
.intellij
.ui
.TreeToolTipHandler
;
51 import com
.intellij
.util
.EventDispatcher
;
52 import com
.intellij
.util
.Icons
;
53 import com
.intellij
.util
.ui
.ThreeStateCheckBox
;
54 import org
.jetbrains
.annotations
.NotNull
;
55 import org
.jetbrains
.annotations
.Nullable
;
58 import javax
.swing
.border
.Border
;
60 import java
.awt
.event
.ActionEvent
;
61 import java
.awt
.event
.ActionListener
;
62 import java
.util
.ArrayList
;
63 import java
.util
.Collections
;
64 import java
.util
.List
;
69 public class ArtifactEditorImpl
implements ArtifactEditorEx
{
70 private JPanel myMainPanel
;
71 private JCheckBox myBuildOnMakeCheckBox
;
72 private TextFieldWithBrowseButton myOutputDirectoryField
;
73 private JPanel myEditorPanel
;
74 private JPanel myErrorPanelPlace
;
75 private ThreeStateCheckBox myShowContentCheckBox
;
76 private FixedSizeButton myShowSpecificContentOptionsButton
;
77 private ActionGroup myShowSpecificContentOptionsGroup
;
78 private Splitter mySplitter
;
79 private final Project myProject
;
80 private final ComplexElementSubstitutionParameters mySubstitutionParameters
= new ComplexElementSubstitutionParameters();
81 private final EventDispatcher
<ArtifactEditorListener
> myDispatcher
= EventDispatcher
.create(ArtifactEditorListener
.class);
82 private final ArtifactEditorContextImpl myContext
;
83 private SourceItemsTree mySourceItemsTree
;
84 private final Artifact myOriginalArtifact
;
85 private final LayoutTreeComponent myLayoutTreeComponent
;
86 private TabbedPaneWrapper myTabbedPane
;
87 private ArtifactPropertiesEditors myPropertiesEditors
;
88 private ArtifactValidationManagerImpl myValidationManager
;
90 public ArtifactEditorImpl(final ArtifactsStructureConfigurableContext context
, Artifact artifact
) {
91 myContext
= new ArtifactEditorContextImpl(context
, this);
92 myOriginalArtifact
= artifact
;
93 myProject
= context
.getProject();
94 mySourceItemsTree
= new SourceItemsTree(myContext
, this);
95 myLayoutTreeComponent
= new LayoutTreeComponent(this, mySubstitutionParameters
, myContext
, myOriginalArtifact
);
96 myPropertiesEditors
= new ArtifactPropertiesEditors(myContext
, myOriginalArtifact
);
97 Disposer
.register(this, mySourceItemsTree
);
98 Disposer
.register(this, myLayoutTreeComponent
);
99 myBuildOnMakeCheckBox
.setSelected(artifact
.isBuildOnMake());
100 final String outputPath
= artifact
.getOutputPath();
101 myOutputDirectoryField
.addBrowseFolderListener(CompilerBundle
.message("dialog.title.output.directory.for.artifact"),
102 CompilerBundle
.message("chooser.description.select.output.directory.for.0.artifact",
103 getArtifact().getName()), myProject
,
104 FileChooserDescriptorFactory
.createSingleFolderDescriptor());
105 myShowSpecificContentOptionsGroup
= createShowSpecificContentOptionsGroup();
106 myShowSpecificContentOptionsButton
.addActionListener(new ActionListener() {
107 public void actionPerformed(ActionEvent e
) {
108 ActionManager
.getInstance().createActionPopupMenu(ActionPlaces
.UNKNOWN
, myShowSpecificContentOptionsGroup
).getComponent().show(myShowSpecificContentOptionsButton
, 0, 0);
111 setOutputPath(outputPath
);
112 myValidationManager
= new ArtifactValidationManagerImpl(this);
113 myContext
.setValidationManager(myValidationManager
);
114 updateShowContentCheckbox();
117 private ActionGroup
createShowSpecificContentOptionsGroup() {
118 final DefaultActionGroup group
= new DefaultActionGroup();
119 for (ComplexPackagingElementType
<?
> type
: PackagingElementFactory
.getInstance().getComplexElementTypes()) {
120 group
.add(new ToggleShowElementContentAction(type
, this));
125 private void setOutputPath(@Nullable String outputPath
) {
126 myOutputDirectoryField
.setText(outputPath
!= null ? FileUtil
.toSystemDependentName(outputPath
) : null);
129 public void apply() {
130 final ModifiableArtifact modifiableArtifact
= myContext
.getModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact
);
131 modifiableArtifact
.setBuildOnMake(myBuildOnMakeCheckBox
.isSelected());
132 modifiableArtifact
.setOutputPath(getConfiguredOutputPath());
133 myPropertiesEditors
.applyProperties();
134 myLayoutTreeComponent
.saveElementProperties();
138 private String
getConfiguredOutputPath() {
139 String outputPath
= FileUtil
.toSystemIndependentName(myOutputDirectoryField
.getText().trim());
140 if (outputPath
.length() == 0) {
146 public SourceItemsTree
getSourceItemsTree() {
147 return mySourceItemsTree
;
150 public void addListener(@NotNull final ArtifactEditorListener listener
) {
151 myDispatcher
.addListener(listener
);
154 public ArtifactEditorContextImpl
getContext() {
158 public void removeListener(@NotNull final ArtifactEditorListener listener
) {
159 myDispatcher
.removeListener(listener
);
162 public Artifact
getArtifact() {
163 return myContext
.getArtifactModel().getArtifactByOriginal(myOriginalArtifact
);
166 public CompositePackagingElement
<?
> getRootElement() {
167 return myLayoutTreeComponent
.getRootElement();
170 public void rebuildTries() {
171 myLayoutTreeComponent
.rebuildTree();
172 mySourceItemsTree
.rebuildTree();
175 public void queueValidation() {
176 myValidationManager
.queueValidation();
179 public JComponent
createMainComponent() {
180 mySourceItemsTree
.initTree();
181 myLayoutTreeComponent
.initTree();
182 myMainPanel
.putClientProperty(DataManager
.CLIENT_PROPERTY_DATA_PROVIDER
, new TypeSafeDataProviderAdapter(new MyDataProvider()));
184 myErrorPanelPlace
.add(myValidationManager
.getMainErrorPanel(), BorderLayout
.CENTER
);
186 mySplitter
= new Splitter(false);
187 final JPanel leftPanel
= new JPanel(new BorderLayout());
188 leftPanel
.add(myLayoutTreeComponent
.getTreePanel(), BorderLayout
.CENTER
);
189 final Border border
= BorderFactory
.createEmptyBorder(3, 3, 3, 3);
190 leftPanel
.setBorder(border
);
191 mySplitter
.setFirstComponent(leftPanel
);
193 final JPanel rightPanel
= new JPanel(new BorderLayout());
194 final JPanel rightTopPanel
= new JPanel(new BorderLayout());
195 rightTopPanel
.add(new JLabel("Available Elements (drag'n'drop to layout tree)"), BorderLayout
.SOUTH
);
196 rightPanel
.add(rightTopPanel
, BorderLayout
.NORTH
);
197 rightPanel
.add(ScrollPaneFactory
.createScrollPane(mySourceItemsTree
.getTree()), BorderLayout
.CENTER
);
198 rightPanel
.add(new JPanel(), BorderLayout
.SOUTH
);
199 rightPanel
.setBorder(border
);
200 mySplitter
.setSecondComponent(rightPanel
);
203 myShowContentCheckBox
.addActionListener(new ActionListener() {
204 public void actionPerformed(ActionEvent e
) {
205 final ThreeStateCheckBox
.State state
= myShowContentCheckBox
.getState();
206 if (state
== ThreeStateCheckBox
.State
.SELECTED
) {
207 mySubstitutionParameters
.setSubstituteAll();
209 else if (state
== ThreeStateCheckBox
.State
.NOT_SELECTED
) {
210 mySubstitutionParameters
.setSubstituteNone();
212 myShowContentCheckBox
.setThirdStateEnabled(false);
213 myLayoutTreeComponent
.rebuildTree();
217 ActionToolbar toolbar
= ActionManager
.getInstance().createActionToolbar(ActionPlaces
.UNKNOWN
, createToolbarActionGroup(), true);
218 leftPanel
.add(toolbar
.getComponent(), BorderLayout
.NORTH
);
219 rightTopPanel
.setPreferredSize(new Dimension(-1, toolbar
.getComponent().getPreferredSize().height
));
221 myTabbedPane
= new TabbedPaneWrapper(this);
222 myTabbedPane
.addTab("Output Layout", mySplitter
);
223 myPropertiesEditors
.addTabs(myTabbedPane
);
224 myEditorPanel
.add(myTabbedPane
.getComponent(), BorderLayout
.CENTER
);
226 final LayoutTree tree
= myLayoutTreeComponent
.getLayoutTree();
227 new ShowAddPackagingElementPopupAction(this).registerCustomShortcutSet(CommonShortcuts
.getNew(), tree
);
228 PopupHandler
.installPopupHandler(tree
, createPopupActionGroup(), ActionPlaces
.UNKNOWN
, ActionManager
.getInstance());
229 TreeToolTipHandler
.install(tree
);
230 ToolTipManager
.sharedInstance().registerComponent(tree
);
232 return getMainComponent();
235 public void updateShowContentCheckbox() {
236 final ThreeStateCheckBox
.State state
;
237 if (mySubstitutionParameters
.isAllSubstituted()) {
238 state
= ThreeStateCheckBox
.State
.SELECTED
;
240 else if (mySubstitutionParameters
.isNoneSubstituted()) {
241 state
= ThreeStateCheckBox
.State
.NOT_SELECTED
;
244 state
= ThreeStateCheckBox
.State
.DONT_CARE
;
246 myShowContentCheckBox
.setThirdStateEnabled(state
== ThreeStateCheckBox
.State
.DONT_CARE
);
247 myShowContentCheckBox
.setState(state
);
250 private DefaultActionGroup
createToolbarActionGroup() {
251 final DefaultActionGroup toolbarActionGroup
= new DefaultActionGroup();
253 final List
<AnAction
> createActions
= new ArrayList
<AnAction
>(createNewElementActions());
254 for (AnAction createAction
: createActions
) {
255 toolbarActionGroup
.add(createAction
);
258 toolbarActionGroup
.add(new RemovePackagingElementAction(this));
259 toolbarActionGroup
.add(Separator
.getInstance());
260 toolbarActionGroup
.add(new SortElementsToggleAction(this.getLayoutTreeComponent()));
261 toolbarActionGroup
.add(new MoveElementAction(myLayoutTreeComponent
, "Move Up", "", IconLoader
.getIcon("/actions/moveUp.png"), -1));
262 toolbarActionGroup
.add(new MoveElementAction(myLayoutTreeComponent
, "Move Down", "", IconLoader
.getIcon("/actions/moveDown.png"), 1));
263 return toolbarActionGroup
;
266 public List
<AnAction
> createNewElementActions() {
267 final List
<AnAction
> createActions
= new ArrayList
<AnAction
>();
268 AddCompositeElementAction
.addCompositeCreateActions(createActions
, this);
269 createActions
.add(createAddNonCompositeElementGroup());
270 return createActions
;
273 private DefaultActionGroup
createPopupActionGroup() {
274 final LayoutTree tree
= myLayoutTreeComponent
.getLayoutTree();
276 DefaultActionGroup popupActionGroup
= new DefaultActionGroup();
277 final List
<AnAction
> createActions
= new ArrayList
<AnAction
>();
278 AddCompositeElementAction
.addCompositeCreateActions(createActions
, this);
279 for (AnAction createAction
: createActions
) {
280 popupActionGroup
.add(createAction
);
282 popupActionGroup
.add(createAddNonCompositeElementGroup());
283 final RemovePackagingElementAction removeAction
= new RemovePackagingElementAction(this);
284 removeAction
.registerCustomShortcutSet(CommonShortcuts
.DELETE
, tree
);
285 popupActionGroup
.add(removeAction
);
286 popupActionGroup
.add(new ExtractArtifactAction(this));
287 popupActionGroup
.add(new InlineArtifactAction(this));
288 popupActionGroup
.add(new RenamePackagingElementAction(this));
289 popupActionGroup
.add(Separator
.getInstance());
290 popupActionGroup
.add(new HideContentAction(this));
291 popupActionGroup
.add(new ArtifactEditorNavigateAction(myLayoutTreeComponent
));
292 popupActionGroup
.add(new ArtifactEditorFindUsagesAction(myLayoutTreeComponent
, myProject
));
294 popupActionGroup
.add(Separator
.getInstance());
295 CommonActionsManager actionsManager
= CommonActionsManager
.getInstance();
296 DefaultTreeExpander treeExpander
= new DefaultTreeExpander(tree
);
297 popupActionGroup
.add(actionsManager
.createExpandAllAction(treeExpander
, tree
));
298 popupActionGroup
.add(actionsManager
.createCollapseAllAction(treeExpander
, tree
));
299 return popupActionGroup
;
302 public ComplexElementSubstitutionParameters
getSubstitutionParameters() {
303 return mySubstitutionParameters
;
306 private ActionGroup
createAddNonCompositeElementGroup() {
307 DefaultActionGroup group
= new DefaultActionGroup(ProjectBundle
.message("artifacts.add.copy.action"), true);
308 group
.getTemplatePresentation().setIcon(Icons
.ADD_ICON
);
309 for (PackagingElementType
<?
> type
: PackagingElementFactory
.getInstance().getNonCompositeElementTypes()) {
310 group
.add(new AddNewPackagingElementAction(type
, this));
315 public JComponent
getMainComponent() {
319 public void addNewPackagingElement(@NotNull PackagingElementType
<?
> type
) {
320 myLayoutTreeComponent
.addNewPackagingElement(type
);
321 mySourceItemsTree
.rebuildTree();
324 public void removeSelectedElements() {
325 myLayoutTreeComponent
.removeSelectedElements();
328 public boolean isModified() {
329 return myBuildOnMakeCheckBox
.isSelected() != myOriginalArtifact
.isBuildOnMake()
330 || !Comparing
.equal(getConfiguredOutputPath(), myOriginalArtifact
.getOutputPath())
331 || myPropertiesEditors
.isModified()
332 || myLayoutTreeComponent
.isPropertiesModified();
335 public void dispose() {
338 public LayoutTreeComponent
getLayoutTreeComponent() {
339 return myLayoutTreeComponent
;
342 public void updateOutputPath(@NotNull String oldArtifactName
, @NotNull final String newArtifactName
) {
343 final String oldDefaultPath
= ArtifactUtil
.getDefaultArtifactOutputPath(oldArtifactName
, myProject
);
344 if (Comparing
.equal(oldDefaultPath
, getConfiguredOutputPath())) {
345 setOutputPath(ArtifactUtil
.getDefaultArtifactOutputPath(newArtifactName
, myProject
));
346 final CompositePackagingElement
<?
> root
= getRootElement();
347 if (root
instanceof ArchivePackagingElement
) {
348 final String name
= ((ArchivePackagingElement
)root
).getArchiveFileName();
349 final String fileName
= FileUtil
.getNameWithoutExtension(name
);
350 final String extension
= FileUtil
.getExtension(name
);
351 if (fileName
.equals(oldArtifactName
) && extension
.length() > 0) {
352 myLayoutTreeComponent
.editLayout(new Runnable() {
354 ((ArchivePackagingElement
)getRootElement()).setArchiveFileName(newArtifactName
+ "." + extension
);
357 myLayoutTreeComponent
.updateTreeNodesPresentation();
363 public void putLibraryIntoDefaultLocation(@NotNull Library library
) {
364 myLayoutTreeComponent
.putIntoDefaultLocations(Collections
.singletonList(new LibrarySourceItem(library
)));
367 public void addToClasspath(CompositePackagingElement
<?
> element
, List
<String
> classpath
) {
368 myLayoutTreeComponent
.saveElementProperties();
369 final ManifestFileConfiguration manifest
= myContext
.getManifestFile(element
, getArtifact().getArtifactType());
370 manifest
.addToClasspath(classpath
);
371 myLayoutTreeComponent
.resetElementProperties();
374 private class MyDataProvider
implements TypeSafeDataProvider
{
375 public void calcData(DataKey key
, DataSink sink
) {
376 if (ARTIFACTS_EDITOR_KEY
.equals(key
)) {
377 sink
.put(ARTIFACTS_EDITOR_KEY
, ArtifactEditorImpl
.this);