blob: 54ec270b746d4c30cfb1ec9386a86bf75fe7dd66 [file] [log] [blame]
/*
* Copyright 2000-2014 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.util.xml.ui;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.xml.XmlElement;
import com.intellij.ui.CommonActionsPanel;
import com.intellij.util.ArrayUtil;
import com.intellij.util.EventDispatcher;
import com.intellij.util.IconUtil;
import com.intellij.util.ReflectionUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.highlighting.DomCollectionProblemDescriptor;
import com.intellij.util.xml.highlighting.DomElementAnnotationsManager;
import com.intellij.util.xml.highlighting.DomElementProblemDescriptor;
import com.intellij.util.xml.reflect.DomCollectionChildDescription;
import com.intellij.util.xml.ui.actions.AddDomElementAction;
import com.intellij.util.xml.ui.actions.DefaultAddAction;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @author peter
*/
public class DomCollectionControl<T extends DomElement> extends DomUIControl implements Highlightable, TypeSafeDataProvider {
private static final DataKey<DomCollectionControl> DOM_COLLECTION_CONTROL = DataKey.create("DomCollectionControl");
private final EventDispatcher<CommitListener> myDispatcher = EventDispatcher.create(CommitListener.class);
private DomTableView myCollectionPanel;
private final DomElement myParentDomElement;
private final DomCollectionChildDescription myChildDescription;
private List<T> myCollectionElements = new ArrayList<T>();
private ColumnInfo<T, ?>[] myColumnInfos;
private boolean myEditable = false;
public static final Icon ADD_ICON = IconUtil.getAddIcon();
public static final Icon EDIT_ICON = IconUtil.getEditIcon();
public static final Icon REMOVE_ICON = IconUtil.getRemoveIcon();
public DomCollectionControl(DomElement parentElement,
DomCollectionChildDescription description,
final boolean editable,
ColumnInfo<T, ?>... columnInfos) {
myChildDescription = description;
myParentDomElement = parentElement;
myColumnInfos = columnInfos;
myEditable = editable;
}
public DomCollectionControl(DomElement parentElement,
@NonNls String subTagName,
final boolean editable,
ColumnInfo<T, ?>... columnInfos) {
this(parentElement, parentElement.getGenericInfo().getCollectionChildDescription(subTagName), editable, columnInfos);
}
public DomCollectionControl(DomElement parentElement, DomCollectionChildDescription description) {
myChildDescription = description;
myParentDomElement = parentElement;
}
public DomCollectionControl(DomElement parentElement, @NonNls String subTagName) {
this(parentElement, parentElement.getGenericInfo().getCollectionChildDescription(subTagName));
}
public boolean isEditable() {
return myEditable;
}
@Override
public void bind(JComponent component) {
assert component instanceof DomTableView;
initialize((DomTableView)component);
}
@Override
public void addCommitListener(CommitListener listener) {
myDispatcher.addListener(listener);
}
@Override
public void removeCommitListener(CommitListener listener) {
myDispatcher.removeListener(listener);
}
@Override
public boolean canNavigate(DomElement element) {
final Class<DomElement> aClass = (Class<DomElement>)ReflectionUtil.getRawType(myChildDescription.getType());
final DomElement domElement = element.getParentOfType(aClass, false);
return domElement != null && myCollectionElements.contains(domElement);
}
@Override
public void navigate(DomElement element) {
final Class<DomElement> aClass = (Class<DomElement>)ReflectionUtil.getRawType(myChildDescription.getType());
final DomElement domElement = element.getParentOfType(aClass, false);
int index = myCollectionElements.indexOf(domElement);
if (index < 0) index = 0;
myCollectionPanel.getTable().setRowSelectionInterval(index, index);
}
@Override
public void calcData(final DataKey key, final DataSink sink) {
if (DOM_COLLECTION_CONTROL.equals(key)) {
sink.put(DOM_COLLECTION_CONTROL, this);
}
}
@Nullable
protected String getHelpId() {
return null;
}
@Nullable
protected String getEmptyPaneText() {
return null;
}
protected void initialize(final DomTableView boundComponent) {
if (boundComponent == null) {
myCollectionPanel = new DomTableView(getProject(), getEmptyPaneText(), getHelpId());
}
else {
myCollectionPanel = boundComponent;
}
myCollectionPanel.setToolbarActions(new AddAction(), new EditAction(), new RemoveAction());
myCollectionPanel.installPopup(ActionPlaces.J2EE_ATTRIBUTES_VIEW_POPUP, createPopupActionGroup());
myCollectionPanel.initializeTable();
myCollectionPanel.addCustomDataProvider(this);
myCollectionPanel.addChangeListener(new AbstractTableView.ChangeListener() {
@Override
public void changed() {
reset();
}
});
reset();
}
protected DefaultActionGroup createPopupActionGroup() {
final DefaultActionGroup group = new DefaultActionGroup();
group.addAll((DefaultActionGroup)ActionManager.getInstance().getAction("DomCollectionControl"));
return group;
}
protected ColumnInfo[] createColumnInfos(DomElement parent) {
return myColumnInfos;
}
protected final void doEdit() {
doEdit(myCollectionElements.get(myCollectionPanel.getTable().getSelectedRow()));
}
protected void doEdit(final T t) {
final DomEditorManager manager = getDomEditorManager(this);
if (manager != null) {
manager.openDomElementEditor(t);
}
}
protected void doRemove(final List<T> toDelete) {
Set<PsiFile> files = new HashSet<PsiFile>();
for (final T t : toDelete) {
final XmlElement element = t.getXmlElement();
if (element != null) {
ContainerUtil.addIfNotNull(element.getContainingFile(), files);
}
}
new WriteCommandAction(getProject(), PsiUtilCore.toPsiFileArray(files)) {
@Override
protected void run(Result result) throws Throwable {
for (final T t : toDelete) {
if (t.isValid()) {
t.undefine();
}
}
}
}.execute();
}
protected final void doRemove() {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
final int[] selected = myCollectionPanel.getTable().getSelectedRows();
if (selected == null || selected.length == 0) return;
final List<T> selectedElements = new ArrayList<T>(selected.length);
for (final int i : selected) {
selectedElements.add(myCollectionElements.get(i));
}
doRemove(selectedElements);
reset();
int selection = selected[0];
if (selection >= myCollectionElements.size()) {
selection = myCollectionElements.size() - 1;
}
if (selection >= 0) {
myCollectionPanel.getTable().setRowSelectionInterval(selection, selection);
}
}
});
}
protected static void performWriteCommandAction(final WriteCommandAction writeCommandAction) {
writeCommandAction.execute();
}
@Override
public void commit() {
final CommitListener listener = myDispatcher.getMulticaster();
listener.beforeCommit(this);
listener.afterCommit(this);
validate();
}
private void validate() {
DomElement domElement = getDomElement();
final List<DomElementProblemDescriptor> list =
DomElementAnnotationsManager.getInstance(getProject()).getCachedProblemHolder(domElement).getProblems(domElement);
final List<String> messages = new ArrayList<String>();
for (final DomElementProblemDescriptor descriptor : list) {
if (descriptor instanceof DomCollectionProblemDescriptor
&& myChildDescription.equals(((DomCollectionProblemDescriptor)descriptor).getChildDescription())) {
messages.add(descriptor.getDescriptionTemplate());
}
}
myCollectionPanel.setErrorMessages(ArrayUtil.toStringArray(messages));
myCollectionPanel.repaint();
}
@Override
public void dispose() {
if (myCollectionPanel != null) {
myCollectionPanel.dispose();
}
}
protected final Project getProject() {
return myParentDomElement.getManager().getProject();
}
@Override
public DomTableView getComponent() {
if (myCollectionPanel == null) initialize(null);
return myCollectionPanel;
}
public final DomCollectionChildDescription getChildDescription() {
return myChildDescription;
}
@Override
public final DomElement getDomElement() {
return myParentDomElement;
}
@Override
public final void reset() {
myCollectionElements = new ArrayList<T>(getCollectionElements());
myCollectionPanel.reset(createColumnInfos(myParentDomElement), myCollectionElements);
validate();
}
public List<T> getCollectionElements() {
return (List<T>)myChildDescription.getValues(myParentDomElement);
}
@Nullable
protected AnAction[] createAdditionActions() {
return null;
}
protected DefaultAddAction createDefaultAction(final String name, final Icon icon, final Type type) {
return new ControlAddAction(name, name, icon) {
@Override
protected Type getElementType() {
return type;
}
};
}
protected final Class<? extends T> getCollectionElementClass() {
return (Class<? extends T>)ReflectionUtil.getRawType(myChildDescription.getType());
}
@Nullable
private static DomEditorManager getDomEditorManager(DomUIControl control) {
JComponent component = control.getComponent();
while (component != null && !(component instanceof DomEditorManager)) {
final Container parent = component.getParent();
if (!(parent instanceof JComponent)) {
return null;
}
component = (JComponent)parent;
}
return (DomEditorManager)component;
}
@Override
public void updateHighlighting() {
if (myCollectionPanel != null) {
myCollectionPanel.revalidate();
myCollectionPanel.repaint();
}
}
public class ControlAddAction extends DefaultAddAction<T> {
public ControlAddAction() {
}
public ControlAddAction(final String text) {
super(text);
}
public ControlAddAction(final String text, final String description, final Icon icon) {
super(text, description, icon);
}
@Override
protected final DomCollectionChildDescription getDomCollectionChildDescription() {
return myChildDescription;
}
@Override
protected final DomElement getParentDomElement() {
return myParentDomElement;
}
/**
* return negative value to disable auto-edit
* @return
*/
protected int getColumnToEditAfterAddition() {
return 0;
}
protected void afterAddition(final JTable table, final int rowIndex) {
table.setRowSelectionInterval(rowIndex, rowIndex);
final int column = getColumnToEditAfterAddition();
if (column >= 0 ) {
table.editCellAt(rowIndex, column);
}
}
@Override
protected final void afterAddition(@NotNull final T newElement) {
reset();
afterAddition(myCollectionPanel.getTable(), myCollectionElements.size() - 1);
}
}
public static DomCollectionControl getDomCollectionControl(final AnActionEvent e) {
return e.getData(DOM_COLLECTION_CONTROL);
}
public static class AddAction extends AddDomElementAction {
public AddAction() {
setShortcutSet(CommonActionsPanel.getCommonShortcut(CommonActionsPanel.Buttons.ADD));
}
@Override
protected boolean isEnabled(final AnActionEvent e) {
return getDomCollectionControl(e) != null;
}
protected DomCollectionControl getDomCollectionControl(final AnActionEvent e) {
return DomCollectionControl.getDomCollectionControl(e);
}
@Override
@NotNull
protected DomCollectionChildDescription[] getDomCollectionChildDescriptions(final AnActionEvent e) {
return new DomCollectionChildDescription[] {getDomCollectionControl(e).getChildDescription()};
}
@Override
protected DomElement getParentDomElement(final AnActionEvent e) {
return getDomCollectionControl(e).getDomElement();
}
@Override
protected JComponent getComponent(AnActionEvent e) {
return getDomCollectionControl(e).getComponent();
}
@Override
@NotNull
public AnAction[] getChildren(final AnActionEvent e) {
final DomCollectionControl control = getDomCollectionControl(e);
AnAction[] actions = control.createAdditionActions();
return actions == null ? super.getChildren(e) : actions;
}
@Override
protected DefaultAddAction createAddingAction(final AnActionEvent e,
final String name,
final Icon icon,
final Type type,
final DomCollectionChildDescription description) {
return getDomCollectionControl(e).createDefaultAction(name, icon, type);
}
}
public static class EditAction extends AnAction {
public EditAction() {
super(ApplicationBundle.message("action.edit"), null, DomCollectionControl.EDIT_ICON);
setShortcutSet(CommonActionsPanel.getCommonShortcut(CommonActionsPanel.Buttons.EDIT));
}
@Override
public void actionPerformed(AnActionEvent e) {
final DomCollectionControl control = DomCollectionControl.getDomCollectionControl(e);
control.doEdit();
control.reset();
}
@Override
public void update(AnActionEvent e) {
final DomCollectionControl control = DomCollectionControl.getDomCollectionControl(e);
final boolean visible = control != null && control.isEditable();
e.getPresentation().setVisible(visible);
e.getPresentation().setEnabled(visible && control.getComponent().getTable().getSelectedRowCount() == 1);
}
}
public static class RemoveAction extends AnAction {
public RemoveAction() {
super(ApplicationBundle.message("action.remove"), null, DomCollectionControl.REMOVE_ICON);
setShortcutSet(CommonActionsPanel.getCommonShortcut(CommonActionsPanel.Buttons.REMOVE));
}
@Override
public void actionPerformed(AnActionEvent e) {
final DomCollectionControl control = DomCollectionControl.getDomCollectionControl(e);
control.doRemove();
control.reset();
}
@Override
public void update(AnActionEvent e) {
final boolean enabled;
final DomCollectionControl control = DomCollectionControl.getDomCollectionControl(e);
if (control != null) {
final JTable table = control.getComponent().getTable();
enabled = table != null && table.getSelectedRowCount() > 0;
} else {
enabled = false;
}
e.getPresentation().setEnabled(enabled);
}
}
}