blob: 71278492234dfada0e5fd6873030be3883d16823 [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.util.importProject;
import com.intellij.icons.AllIcons;
import com.intellij.ide.util.ElementsChooser;
import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.InputValidator;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.ui.ex.MultiLineLabel;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.components.JBList;
import com.intellij.util.IconUtil;
import com.intellij.util.PlatformIcons;
import com.intellij.util.StringBuilderSpinAllocator;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.FormBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.io.File;
import java.util.*;
import java.util.List;
/**
* @author Eugene Zhuravlev
* Date: Jul 16, 2007
*/
abstract class ProjectLayoutPanel<T> extends JPanel {
private final ElementsChooser<T> myEntriesChooser;
private final JList myDependenciesList;
private final ModuleInsight myInsight;
private final Comparator<T> COMPARATOR = new Comparator<T>() {
public int compare(final T o1, final T o2) {
final int w1 = getWeight(o1);
final int w2 = getWeight(o2);
if (w1 != w2) {
return w1 - w2;
}
return getElementText(o1).compareToIgnoreCase(getElementText(o2));
}
};
public ProjectLayoutPanel(final ModuleInsight insight) {
super(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
myInsight = insight;
myEntriesChooser = new ElementsChooser<T>(true) {
public String getItemText(@NotNull T element) {
return getElementText(element);
}
};
myDependenciesList = createList();
final Splitter splitter = new Splitter(false);
final JPanel entriesPanel = new JPanel(new BorderLayout());
entriesPanel.add(myEntriesChooser, BorderLayout.CENTER);
entriesPanel.setBorder(IdeBorderFactory.createTitledBorder(StringUtil.capitalize(StringUtil.pluralize(getElementTypeName())), false));
splitter.setFirstComponent(entriesPanel);
final JScrollPane depsPane = ScrollPaneFactory.createScrollPane(myDependenciesList);
final JPanel depsPanel = new JPanel(new BorderLayout());
depsPanel.add(depsPane, BorderLayout.CENTER);
depsPanel.setBorder(IdeBorderFactory.createTitledBorder(getDependenciesTitle(), false));
splitter.setSecondComponent(depsPanel);
JPanel groupPanel = new JPanel(new BorderLayout());
groupPanel.add(createEntriesActionToolbar().getComponent(), BorderLayout.NORTH);
groupPanel.add(splitter, BorderLayout.CENTER);
groupPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
final MultiLineLabel description = new MultiLineLabel(getStepDescriptionText());
add(description, BorderLayout.NORTH);
add(groupPanel, BorderLayout.CENTER);
myEntriesChooser.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(final ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
final List<T> entries = getSelectedEntries();
final Collection deps = getDependencies(entries);
final DefaultListModel depsModel = (DefaultListModel)myDependenciesList.getModel();
depsModel.clear();
for (Object dep : alphaSortList(new ArrayList(deps))) {
depsModel.addElement(dep);
}
}
});
}
private ActionToolbar createEntriesActionToolbar() {
final DefaultActionGroup entriesActions = new DefaultActionGroup();
final RenameAction rename = new RenameAction();
rename.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F6, KeyEvent.SHIFT_DOWN_MASK)), this);
entriesActions.add(rename);
final MergeAction merge = new MergeAction();
merge.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0)), this);
entriesActions.add(merge);
final SplitAction split = new SplitAction();
split.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0)), this);
entriesActions.add(split);
return ActionManager.getInstance().createActionToolbar("ProjectLayoutPanel.Entries", entriesActions, true);
}
public final ModuleInsight getInsight() {
return myInsight;
}
private JList createList() {
final JList list = new JBList(new DefaultListModel());
list.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
list.setCellRenderer(new MyListCellRenderer());
return list;
}
public final Collection getDependencies(final List<T> entries) {
final Set deps = new HashSet();
for (T et : entries) {
deps.addAll(getDependencies(et));
}
return deps;
}
@NotNull
public List<T> getSelectedEntries() {
return myEntriesChooser.getSelectedElements();
}
@NotNull
public List<T> getChosenEntries() {
return myEntriesChooser.getMarkedElements();
}
public void rebuild() {
myEntriesChooser.clear();
for (final T entry : alphaSortList(getEntries())) {
myEntriesChooser.addElement(entry, true, new EntryProperties(entry));
}
if (myEntriesChooser.getElementCount() > 0) {
myEntriesChooser.selectElements(Collections.singleton(myEntriesChooser.getElementAt(0)));
}
}
private List<T> alphaSortList(final List<T> entries) {
Collections.sort(entries, COMPARATOR);
return entries;
}
@Nullable
protected Icon getElementIcon(Object element) {
if (element instanceof ModuleDescriptor) {
return ((ModuleDescriptor)element).getModuleType().getIcon();
}
if (element instanceof LibraryDescriptor) {
return PlatformIcons.LIBRARY_ICON;
}
if (element instanceof File) {
final File file = (File)element;
return file.isDirectory()? PlatformIcons.DIRECTORY_CLOSED_ICON : PlatformIcons.JAR_ICON;
}
return null;
}
protected int getWeight(Object element) {
if (element instanceof File) {
return 10;
}
if (element instanceof ModuleDescriptor) {
return 20;
}
if (element instanceof LibraryDescriptor) {
return ((LibraryDescriptor)element).getJars().size() > 1? 30 : 40;
}
return Integer.MAX_VALUE;
}
protected static String getElementText(Object element) {
if (element instanceof LibraryDescriptor) {
final StringBuilder builder = StringBuilderSpinAllocator.alloc();
try {
builder.append(((LibraryDescriptor)element).getName());
final Collection<File> jars = ((LibraryDescriptor)element).getJars();
if (jars.size() == 1) {
final File parentFile = jars.iterator().next().getParentFile();
if (parentFile != null) {
builder.append(" (");
builder.append(parentFile.getPath());
builder.append(")");
}
}
return builder.toString();
}
finally {
StringBuilderSpinAllocator.dispose(builder);
}
}
if (element instanceof File) {
final StringBuilder builder = StringBuilderSpinAllocator.alloc();
try {
builder.append(((File)element).getName());
final File parentFile = ((File)element).getParentFile();
if (parentFile != null) {
builder.append(" (");
builder.append(parentFile.getPath());
builder.append(")");
}
return builder.toString();
}
finally {
StringBuilderSpinAllocator.dispose(builder);
}
}
if (element instanceof ModuleDescriptor) {
final ModuleDescriptor moduleDescriptor = (ModuleDescriptor)element;
final StringBuilder builder = StringBuilderSpinAllocator.alloc();
try {
builder.append(moduleDescriptor.getName());
final Set<File> contents = moduleDescriptor.getContentRoots();
final int rootCount = contents.size();
if (rootCount > 0) {
builder.append(" (");
builder.append(contents.iterator().next().getPath());
if (rootCount > 1) {
builder.append("...");
}
builder.append(")");
}
final Collection<? extends DetectedProjectRoot> sourceRoots = moduleDescriptor.getSourceRoots();
if (sourceRoots.size() > 0) {
builder.append(" [");
for (Iterator<? extends DetectedProjectRoot> it = sourceRoots.iterator(); it.hasNext();) {
DetectedProjectRoot root = it.next();
builder.append(root.getDirectory().getName());
if (it.hasNext()) {
builder.append(",");
}
}
builder.append("]");
}
return builder.toString();
}
finally {
StringBuilderSpinAllocator.dispose(builder);
}
}
return "";
}
protected abstract List<T> getEntries();
protected abstract Collection getDependencies(T entry);
@Nullable
protected abstract T merge(List<T> entries);
@Nullable
protected abstract T split(T entry, String newEntryName, Collection<File> extractedData);
protected abstract Collection<File> getContent(T entry);
protected abstract String getElementName(T entry);
protected abstract void setElementName(T entry, String name);
protected abstract String getSplitDialogChooseFilesPrompt();
protected abstract String getNameAlreadyUsedMessage(final String name);
protected abstract String getStepDescriptionText();
protected abstract String getEntriesChooserTitle();
protected abstract String getDependenciesTitle();
protected abstract String getElementTypeName();
private boolean isNameAlreadyUsed(String entryName) {
final Set<T> itemsToIgnore = new HashSet<T>(myEntriesChooser.getSelectedElements());
for (T entry : getEntries()) {
if (itemsToIgnore.contains(entry)) {
continue;
}
if (entryName.equals(getElementName(entry))) {
return true;
}
}
return false;
}
private class MergeAction extends AnAction {
private MergeAction() {
super("Merge", "", AllIcons.Modules.Merge); // todo
}
public void actionPerformed(final AnActionEvent e) {
final List<T> elements = myEntriesChooser.getSelectedElements();
if (elements.size() > 1) {
final String newName = Messages.showInputDialog(
ProjectLayoutPanel.this,
"Enter new name for merge result:",
"Merge",
Messages.getQuestionIcon(), getElementName(elements.get(0)), new InputValidator() {
public boolean checkInput(final String inputString) {
return true;
}
public boolean canClose(final String inputString) {
if (isNameAlreadyUsed(inputString.trim())) {
Messages.showErrorDialog(getNameAlreadyUsedMessage(inputString), "");
return false;
}
return true;
}
});
if (newName != null) {
final T merged = merge(elements);
setElementName(merged, newName);
for (T element : elements) {
myEntriesChooser.removeElement(element);
}
myEntriesChooser.addElement(merged, true, new EntryProperties(merged));
myEntriesChooser.sort(COMPARATOR);
myEntriesChooser.selectElements(Collections.singleton(merged));
}
}
}
public void update(final AnActionEvent e) {
super.update(e);
e.getPresentation().setEnabled(myEntriesChooser.getSelectedElements().size() > 1);
}
}
private class SplitAction extends AnAction {
private SplitAction() {
super("Split", "", AllIcons.Modules.Split); // todo
}
public void actionPerformed(final AnActionEvent e) {
final List<T> elements = myEntriesChooser.getSelectedElements();
if (elements.size() == 1) {
final T entry = elements.get(0);
final Collection<File> files = getContent(entry);
final SplitDialog dialog = new SplitDialog(files);
dialog.show();
if (dialog.isOK()) {
final String newName = dialog.getName();
final Collection<File> chosenFiles = dialog.getChosenFiles();
final T extracted = split(entry, newName, chosenFiles);
if (extracted != null) {
if (!getEntries().contains(entry)) {
myEntriesChooser.removeElement(entry);
}
myEntriesChooser.addElement(extracted, true, new EntryProperties(extracted));
myEntriesChooser.sort(COMPARATOR);
myEntriesChooser.selectElements(Collections.singleton(extracted));
}
}
}
}
public void update(final AnActionEvent e) {
final List<T> elements = myEntriesChooser.getSelectedElements();
e.getPresentation().setEnabled(elements.size() == 1 && getContent(elements.get(0)).size() > 1);
}
}
private class RenameAction extends AnAction {
private RenameAction() {
super("Rename", "", IconUtil.getEditIcon()); // todo
}
public void actionPerformed(final AnActionEvent e) {
final List<T> elements = myEntriesChooser.getSelectedElements();
if (elements.size() == 1) {
final T element = elements.get(0);
final String newName = Messages.showInputDialog(ProjectLayoutPanel.this, "New name for " + getElementTypeName() + " '" + getElementName(element) + "':",
"Rename " + StringUtil.capitalize(getElementTypeName()),
Messages.getQuestionIcon(),
getElementName(element),
new InputValidator() {
public boolean checkInput(final String inputString) {
return true;
}
public boolean canClose(final String inputString) {
if (isNameAlreadyUsed(inputString.trim())) {
Messages.showErrorDialog(getNameAlreadyUsedMessage(inputString), "");
return false;
}
return true;
}
}
);
if (newName != null) {
setElementName(element, newName);
myEntriesChooser.sort(COMPARATOR);
myEntriesChooser.selectElements(Collections.singleton(element));
}
}
}
public void update(final AnActionEvent e) {
super.update(e);
e.getPresentation().setEnabled(myEntriesChooser.getSelectedElements().size() == 1);
}
}
private class MyListCellRenderer extends DefaultListCellRenderer {
public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus) {
final Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
setText(getElementText(value));
setIcon(getElementIcon(value));
return comp;
}
}
private class SplitDialog extends DialogWrapper {
final JTextField myNameField;
final ElementsChooser<File> myChooser;
private SplitDialog(final Collection<File> files) {
super(myEntriesChooser, true);
setTitle("Split " + StringUtil.capitalize(getElementTypeName()));
myNameField = new JTextField();
myChooser = new ElementsChooser<File>(true) {
protected String getItemText(@NotNull final File value) {
return getElementText(value);
}
};
for (final File file : files) {
myChooser.addElement(file, false, new ElementsChooser.ElementProperties() {
public Icon getIcon() {
return getElementIcon(file);
}
public Color getColor() {
return null;
}
});
}
myChooser.selectElements(ContainerUtil.createMaybeSingletonList(ContainerUtil.getFirstItem(files)));
myChooser.addElementsMarkListener(new ElementsChooser.ElementsMarkListener<File>() {
@Override
public void elementMarkChanged(File element, boolean isMarked) {
updateOkButton();
}
});
myNameField.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
updateOkButton();
}
});
init();
updateOkButton();
}
private void updateOkButton() {
setOKActionEnabled(!getName().isEmpty() && !getChosenFiles().isEmpty());
}
protected void doOKAction() {
final String name = getName();
if (isNameAlreadyUsed(name)) {
Messages.showErrorDialog(getNameAlreadyUsedMessage(name), "");
return;
}
super.doOKAction();
}
@Nullable
protected JComponent createCenterPanel() {
FormBuilder builder = FormBuilder.createFormBuilder().setVertical(true);
builder.addLabeledComponent("&Name:", myNameField);
builder.addLabeledComponent(getSplitDialogChooseFilesPrompt(), myChooser);
myChooser.setPreferredSize(new Dimension(450, 300));
return builder.getPanel();
}
public JComponent getPreferredFocusedComponent() {
return myNameField;
}
public String getName() {
return myNameField.getText().trim();
}
public Collection<File> getChosenFiles() {
return myChooser.getMarkedElements();
}
}
private class EntryProperties implements ElementsChooser.ElementProperties {
private final T myEntry;
public EntryProperties(final T entry) {
myEntry = entry;
}
public Icon getIcon() {
return getElementIcon(myEntry);
}
public Color getColor() {
return null;
}
}
}