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
;
63 import javax
.swing
.border
.Border
;
64 import javax
.swing
.event
.HyperlinkEvent
;
65 import javax
.swing
.event
.HyperlinkListener
;
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
;
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));
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();
149 private String
getConfiguredOutputPath() {
150 String outputPath
= FileUtil
.toSystemIndependentName(myOutputDirectoryField
.getText().trim());
151 if (outputPath
.length() == 0) {
157 public SourceItemsTree
getSourceItemsTree() {
158 return mySourceItemsTree
;
161 public void addListener(@NotNull final ArtifactEditorListener listener
) {
162 myDispatcher
.addListener(listener
);
165 public ArtifactEditorContextImpl
getContext() {
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
);
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
;
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));
352 public JComponent
getMainComponent() {
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() {
376 public boolean isDisposed() {
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() {
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
);
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);