| /* |
| * 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; |
| } |
| } |
| } |