multiselection in 'Build Artifacts' popup (IDEA-77450)

This commit is contained in:
nik
2012-08-27 10:50:33 +04:00
parent c85075d619
commit 7c2ec369ed
5 changed files with 190 additions and 71 deletions
@@ -434,9 +434,9 @@ public class CompilerManagerImpl extends CompilerManager {
}
private class ListenerNotificator implements CompileStatusNotification {
private final CompileStatusNotification myDelegate;
private final @Nullable CompileStatusNotification myDelegate;
private ListenerNotificator(CompileStatusNotification delegate) {
private ListenerNotificator(@Nullable CompileStatusNotification delegate) {
myDelegate = delegate;
}
@@ -30,8 +30,10 @@ import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListSeparator;
import com.intellij.openapi.ui.popup.MultiSelectionListPopupStep;
import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
@@ -42,6 +44,7 @@ import com.intellij.packaging.impl.compiler.ArtifactsWorkspaceSettings;
import com.intellij.ui.popup.list.ListPopupImpl;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.EmptyIcon;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -72,17 +75,21 @@ public class BuildArtifactAction extends AnAction {
final List<Artifact> artifacts = ArtifactUtil.getArtifactWithOutputPaths(project);
if (artifacts.isEmpty()) return;
final Artifact first = artifacts.get(0);
List<ArtifactPopupItem> items = new ArrayList<ArtifactPopupItem>();
if (artifacts.size() > 1) {
items.add(0, new ArtifactPopupItem(null, "All Artifacts", EmptyIcon.ICON_16));
}
final Artifact selectedArtifact = ContainerUtil.getFirstItem(ArtifactsWorkspaceSettings.getInstance(project).getArtifactsToBuild());
ArtifactPopupItem selectedItem = null;
Set<Artifact> selectedArtifacts = new HashSet<Artifact>(ArtifactsWorkspaceSettings.getInstance(project).getArtifactsToBuild());
TIntArrayList selectedIndices = new TIntArrayList();
if (Comparing.haveEqualElements(artifacts, selectedArtifacts) && selectedArtifacts.size() > 1) {
selectedIndices.add(0);
selectedArtifacts.clear();
}
for (Artifact artifact : artifacts) {
final ArtifactPopupItem item = new ArtifactPopupItem(artifact, artifact.getName(), artifact.getArtifactType().getIcon());
if (artifact.equals(selectedArtifact)) {
selectedItem = item;
if (selectedArtifacts.contains(artifact)) {
selectedIndices.add(items.size());
}
items.add(item);
}
@@ -90,30 +97,29 @@ public class BuildArtifactAction extends AnAction {
final ProjectSettingsService projectSettingsService = ProjectSettingsService.getInstance(project);
final ArtifactAwareProjectSettingsService settingsService = projectSettingsService instanceof ArtifactAwareProjectSettingsService ? (ArtifactAwareProjectSettingsService)projectSettingsService : null;
final ChooseArtifactStep step = new ChooseArtifactStep(items, first, project, settingsService);
if (selectedItem != null) {
step.setDefaultOptionIndex(items.indexOf(selectedItem));
}
final ChooseArtifactStep step = new ChooseArtifactStep(items, artifacts.get(0), project, settingsService);
step.setDefaultOptionIndices(selectedIndices.toNativeArray());
final ListPopupImpl popup = (ListPopupImpl)JBPopupFactory.getInstance().createListPopup(step);
final KeyStroke editKeyStroke = KeymapUtil.getKeyStroke(CommonShortcuts.getEditSource());
if (settingsService != null && editKeyStroke != null) {
popup.registerAction("editArtifact", editKeyStroke, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
final ArtifactPopupItem item = (ArtifactPopupItem)popup.getSelectedValue();
Object[] values = popup.getSelectedValues();
popup.cancel();
settingsService.openArtifactSettings(item.getArtifact());
settingsService.openArtifactSettings(values.length > 0 ? ((ArtifactPopupItem)values[0]).getArtifact() : null);
}
});
}
popup.showCenteredInCurrentWindow(project);
}
protected static void doBuild(@NotNull Project project, final @NotNull ArtifactPopupItem item, boolean rebuild) {
final List<Artifact> artifacts = item.getArtifacts(project);
protected static void doBuild(@NotNull Project project, final @NotNull List<ArtifactPopupItem> items, boolean rebuild) {
final Set<Artifact> artifacts = getArtifacts(items, project);
final CompileScope scope = ArtifactCompileScope.createArtifactsScope(project, artifacts);
ArtifactsWorkspaceSettings.getInstance(project).setArtifactsToBuild(ContainerUtil.createMaybeSingletonList(item.getArtifact()));
ArtifactsWorkspaceSettings.getInstance(project).setArtifactsToBuild(artifacts);
if (!rebuild) {
CompilerManager.getInstance(project).make(scope, null);
}
@@ -122,19 +128,27 @@ public class BuildArtifactAction extends AnAction {
}
}
private static Set<Artifact> getArtifacts(final List<ArtifactPopupItem> items, final Project project) {
Set<Artifact> artifacts = new LinkedHashSet<Artifact>();
for (ArtifactPopupItem item : items) {
artifacts.addAll(item.getArtifacts(project));
}
return artifacts;
}
private static class BuildArtifactItem extends ArtifactActionItem {
private BuildArtifactItem(ArtifactPopupItem item, Project project) {
private BuildArtifactItem(List<ArtifactPopupItem> item, Project project) {
super(item, project, "Build");
}
@Override
public void run() {
doBuild(myProject, myArtifactPopupItem, false);
doBuild(myProject, myArtifactPopupItems, false);
}
}
private static class CleanArtifactItem extends ArtifactActionItem {
private CleanArtifactItem(@NotNull ArtifactPopupItem item, @NotNull Project project) {
private CleanArtifactItem(@NotNull List<ArtifactPopupItem> item, @NotNull Project project) {
super(item, project, "Clean");
}
@@ -152,7 +166,8 @@ public class BuildArtifactAction extends AnAction {
Map<String, String> outputPathContainingSourceRoots = new HashMap<String, String>();
final List<File> files = new ArrayList<File>();
for (Artifact artifact : myArtifactPopupItem.getArtifacts(myProject)) {
Set<Artifact> artifacts = getArtifacts(myArtifactPopupItems, myProject);
for (Artifact artifact : artifacts) {
String outputPath = artifact.getOutputFilePath();
if (outputPath != null) {
files.add(new File(FileUtil.toSystemDependentName(outputPath)));
@@ -198,37 +213,37 @@ public class BuildArtifactAction extends AnAction {
}
private static class RebuildArtifactItem extends ArtifactActionItem {
private RebuildArtifactItem(ArtifactPopupItem item, Project project) {
private RebuildArtifactItem(List<ArtifactPopupItem> item, Project project) {
super(item, project, "Rebuild");
}
@Override
public void run() {
doBuild(myProject, myArtifactPopupItem, true);
doBuild(myProject, myArtifactPopupItems, true);
}
}
private static class EditArtifactItem extends ArtifactActionItem {
private final ArtifactAwareProjectSettingsService mySettingsService;
private EditArtifactItem(ArtifactPopupItem item, Project project, final ArtifactAwareProjectSettingsService projectSettingsService) {
private EditArtifactItem(List<ArtifactPopupItem> item, Project project, final ArtifactAwareProjectSettingsService projectSettingsService) {
super(item, project, "Edit...");
mySettingsService = projectSettingsService;
}
@Override
public void run() {
mySettingsService.openArtifactSettings(myArtifactPopupItem.getArtifact());
mySettingsService.openArtifactSettings(myArtifactPopupItems.get(0).getArtifact());
}
}
private static abstract class ArtifactActionItem implements Runnable {
protected final ArtifactPopupItem myArtifactPopupItem;
protected final List<ArtifactPopupItem> myArtifactPopupItems;
protected final Project myProject;
private String myActionName;
protected ArtifactActionItem(@NotNull ArtifactPopupItem item, @NotNull Project project, @NotNull String name) {
myArtifactPopupItem = item;
protected ArtifactActionItem(@NotNull List<ArtifactPopupItem> item, @NotNull Project project, @NotNull String name) {
myArtifactPopupItems = item;
myProject = project;
myActionName = name;
}
@@ -243,7 +258,7 @@ public class BuildArtifactAction extends AnAction {
private final String myText;
private final Icon myIcon;
private ArtifactPopupItem(Artifact artifact, String text, Icon icon) {
private ArtifactPopupItem(@Nullable Artifact artifact, String text, Icon icon) {
myArtifact = artifact;
myText = text;
myIcon = icon;
@@ -268,7 +283,7 @@ public class BuildArtifactAction extends AnAction {
}
}
private static class ChooseArtifactStep extends BaseListPopupStep<ArtifactPopupItem> {
private static class ChooseArtifactStep extends MultiSelectionListPopupStep<ArtifactPopupItem> {
private final Artifact myFirst;
private final Project myProject;
private ArtifactAwareProjectSettingsService mySettingsService;
@@ -299,7 +314,7 @@ public class BuildArtifactAction extends AnAction {
}
@Override
public boolean hasSubstep(ArtifactPopupItem selectedValue) {
public boolean hasSubstep(List<ArtifactPopupItem> selectedValues) {
return true;
}
@@ -309,23 +324,23 @@ public class BuildArtifactAction extends AnAction {
}
@Override
public PopupStep onChosen(final ArtifactPopupItem selectedValue, boolean finalChoice) {
public PopupStep<?> onChosen(final List<ArtifactPopupItem> selectedValues, boolean finalChoice) {
if (finalChoice) {
return doFinalStep(new Runnable() {
@Override
public void run() {
doBuild(myProject, selectedValue, false);
doBuild(myProject, selectedValues, false);
}
});
}
final List<ArtifactActionItem> actions = new ArrayList<ArtifactActionItem>();
actions.add(new BuildArtifactItem(selectedValue, myProject));
actions.add(new RebuildArtifactItem(selectedValue, myProject));
actions.add(new CleanArtifactItem(selectedValue, myProject));
actions.add(new BuildArtifactItem(selectedValues, myProject));
actions.add(new RebuildArtifactItem(selectedValues, myProject));
actions.add(new CleanArtifactItem(selectedValues, myProject));
if (mySettingsService != null) {
actions.add(new EditArtifactItem(selectedValue, myProject, mySettingsService));
actions.add(new EditArtifactItem(selectedValues, myProject, mySettingsService));
}
return new BaseListPopupStep<ArtifactActionItem>("Action", actions) {
return new BaseListPopupStep<ArtifactActionItem>(selectedValues.size() == 1 ? "Action" : "Action for " + selectedValues.size() + " artifacts", actions) {
@NotNull
@Override
public String getTextFor(ArtifactActionItem value) {
@@ -176,7 +176,7 @@ public abstract class CompilerManager {
* @param scope a scope to be compiled
* @param callback a notification callback, or null if no notifications needed
*/
public abstract void compile(@NotNull CompileScope scope, CompileStatusNotification callback);
public abstract void compile(@NotNull CompileScope scope, @Nullable CompileStatusNotification callback);
/**
* Compile all modified files and all files that depend on them all over the project.
@@ -0,0 +1,68 @@
/*
* Copyright 2000-2012 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.openapi.ui.popup;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
/**
* @author nik
*/
public abstract class MultiSelectionListPopupStep<T> extends BaseListPopupStep<T> {
private int[] myDefaultOptionIndices = ArrayUtil.EMPTY_INT_ARRAY;
protected MultiSelectionListPopupStep(@Nullable String aTitle, List<? extends T> aValues) {
super(aTitle, aValues);
}
public abstract PopupStep<?> onChosen(List<T> selectedValues, boolean finalChoice);
public boolean hasSubstep(List<T> selectedValues) {
return false;
}
@Override
public final PopupStep onChosen(T selectedValue, boolean finalChoice) {
return onChosen(Collections.singletonList(selectedValue), finalChoice);
}
@Override
public final boolean hasSubstep(T selectedValue) {
return hasSubstep(Collections.singletonList(selectedValue));
}
@Override
public final int getDefaultOptionIndex() {
return myDefaultOptionIndices.length > 0 ? myDefaultOptionIndices[0] : -1;
}
@Override
public final void setDefaultOptionIndex(int aDefaultOptionIndex) {
myDefaultOptionIndices = new int[]{aDefaultOptionIndex};
}
public int[] getDefaultOptionIndices() {
return myDefaultOptionIndices;
}
public void setDefaultOptionIndices(int[] defaultOptionIndices) {
myDefaultOptionIndices = defaultOptionIndices;
}
}
@@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.ui.popup.ListPopupStep;
import com.intellij.openapi.ui.popup.MultiSelectionListPopupStep;
import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.statistics.StatisticsInfo;
@@ -37,6 +38,7 @@ import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
public class ListPopupImpl extends WizardPopup implements ListPopup {
@@ -59,12 +61,11 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
}
public ListPopupImpl(ListPopupStep aStep) {
super(aStep);
this(aStep, -1);
}
public ListPopupImpl(WizardPopup aParent, ListPopupStep aStep, Object parentValue) {
super(aParent, aStep);
setParentValue(parentValue);
this(aParent, aStep, parentValue, -1);
}
public ListPopupImpl(WizardPopup aParent, ListPopupStep aStep, Object parentValue, int maxRowCount) {
@@ -99,15 +100,29 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
}
private boolean tryToAutoSelect(boolean handleFinalChoices) {
final int defaultIndex = getListStep().getDefaultOptionIndex();
if (defaultIndex >= 0 && defaultIndex < myList.getModel().getSize()) {
ListScrollingUtil.selectItem(myList, defaultIndex);
ListPopupStep<Object> listStep = getListStep();
boolean selected = false;
if (listStep instanceof MultiSelectionListPopupStep<?>) {
int[] indices = ((MultiSelectionListPopupStep)listStep).getDefaultOptionIndices();
if (indices.length > 0) {
ListScrollingUtil.ensureIndexIsVisible(myList, indices[0], 0);
myList.setSelectedIndices(indices);
selected = true;
}
}
else {
final int defaultIndex = listStep.getDefaultOptionIndex();
if (defaultIndex >= 0 && defaultIndex < myList.getModel().getSize()) {
ListScrollingUtil.selectItem(myList, defaultIndex);
selected = true;
}
}
if (!selected) {
selectFirstSelectableItem();
}
if (getListStep().isAutoSelectionEnabled()) {
if (listStep.isAutoSelectionEnabled()) {
if (!isVisible() && getSelectableCount() == 1) {
return _handleSelect(handleFinalChoices, null);
} else if (isVisible() && hasSingleSelectableItemWithSubmenu()) {
@@ -196,7 +211,7 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
if (myStep.getTitle() != null) {
myList.getAccessibleContext().setAccessibleName(myStep.getTitle());
}
myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
myList.setSelectionMode(isMultiSelectionEnabled() ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : ListSelectionModel.SINGLE_SELECTION);
myList.setSelectedIndex(0);
Insets padding = UIUtil.getListViewportPadding();
@@ -235,6 +250,10 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
return myList;
}
private boolean isMultiSelectionEnabled() {
return getListStep() instanceof MultiSelectionListPopupStep<?>;
}
private boolean isClosableByLeftArrow() {
return getParent() != null || myStep instanceof ClosableByLeftArrow;
}
@@ -302,10 +321,12 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
return false;
}
final Object selectedValue = myList.getSelectedValue();
if (!getListStep().isSelectable(selectedValue)) return false;
final Object[] selectedValues = myList.getSelectedValues();
final ListPopupStep<Object> listStep = getListStep();
if (!listStep.isSelectable(selectedValues[0])) return false;
if (!getListStep().hasSubstep(selectedValue) && !handleFinalChoices) return false;
if ((listStep instanceof MultiSelectionListPopupStep<?> && !((MultiSelectionListPopupStep<Object>)listStep).hasSubstep(Arrays.asList(selectedValues))
|| !listStep.hasSubstep(selectedValues[0])) && !handleFinalChoices) return false;
disposeChildren();
@@ -317,16 +338,20 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
return true;
}
valueSelected(selectedValue);
valuesSelected(selectedValues);
return handleNextStep(myStep.onChosen(selectedValue, handleFinalChoices), selectedValue, e);
final PopupStep nextStep = listStep instanceof MultiSelectionListPopupStep<?> ? ((MultiSelectionListPopupStep<Object>)listStep).onChosen(Arrays.asList(selectedValues), handleFinalChoices)
: listStep.onChosen(selectedValues[0], handleFinalChoices);
return handleNextStep(nextStep, selectedValues.length == 1 ? selectedValues[0] : null, e);
}
private void valueSelected(final Object value) {
private void valuesSelected(final Object[] values) {
final String filter = getSpeedSearch().getFilter();
if (!StringUtil.isEmpty(filter)) {
final String text = getListStep().getTextFor(value);
StatisticsManager.getInstance().incUseCount(new StatisticsInfo("#list_popup:" + getListStep().getTitle() + "#" + filter, text));
for (Object value : values) {
final String text = getListStep().getTextFor(value);
StatisticsManager.getInstance().incUseCount(new StatisticsInfo("#list_popup:" + getListStep().getTitle() + "#" + filter, text));
}
}
}
@@ -344,7 +369,7 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
assert container != null : "container == null";
int y = point.y;
if (getListModel().isSeparatorAboveOf(parentValue)) {
if (parentValue != null && getListModel().isSeparatorAboveOf(parentValue)) {
SeparatorWithText swt = new SeparatorWithText();
swt.setCaption(getListModel().getCaptionAboveOf(parentValue));
y += swt.getPreferredSize().height - 1;
@@ -370,13 +395,18 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
}
private class MyMouseMotionListener extends MouseMotionAdapter {
private int myLastSelectedIndex = -2;
public void mouseMoved(MouseEvent e) {
Point point = e.getPoint();
int index = myList.locationToIndex(point);
if (index != myList.getSelectedIndex()) {
myList.setSelectedIndex(index);
if (index != myLastSelectedIndex) {
if (!isMultiSelectionEnabled() || !UIUtil.isSelectionButtonDown(e) && myList.getSelectedIndices().length <= 1) {
myList.setSelectedIndex(index);
}
restartTimer();
myLastSelectedIndex = index;
}
notifyParentOnChildSelection();
@@ -387,15 +417,15 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
return UIUtil.isActionClick(e, MouseEvent.MOUSE_RELEASED, true);
}
public Object getSelectedValue() {
return myList.getSelectedValue();
public Object[] getSelectedValues() {
return myList.getSelectedValues();
}
private class MyMouseListener extends MouseAdapter {
@Override
public void mouseReleased(MouseEvent e) {
if (!isActionClick(e)) return;
if (!isActionClick(e) || isMultiSelectionEnabled() && UIUtil.isSelectionButtonDown(e)) return;
IdeEventQueue.getInstance().blockNextEvents(e); // sometimes, after popup close, MOUSE_RELEASE event delivers to other components
final Object selectedValue = myList.getSelectedValue();
final ListPopupStep<Object> listStep = getListStep();
@@ -405,16 +435,14 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
}
protected boolean handleFinalChoices(MouseEvent e, Object selectedValue, ListPopupStep<Object> listStep) {
boolean handleFinalChoices = true;
if (selectedValue != null && listStep.hasSubstep(selectedValue) && listStep.isSelectable(selectedValue)) {
final int index = myList.getSelectedIndex();
final Rectangle bounds = myList.getCellBounds(index, index);
final Point point = e.getPoint();
if (point.getX() > bounds.width + bounds.getX() - AllIcons.Icons.Ide.NextStep.getIconWidth()) { //press on handle icon
handleFinalChoices = false;
}
}
return handleFinalChoices;
return selectedValue == null || !listStep.hasSubstep(selectedValue) || !listStep.isSelectable(selectedValue) || !isOnNextStepButton(e);
}
private boolean isOnNextStepButton(MouseEvent e) {
final int index = myList.getSelectedIndex();
final Rectangle bounds = myList.getCellBounds(index, index);
final Point point = e.getPoint();
return point.getX() > bounds.width + bounds.getX() - AllIcons.Icons.Ide.NextStep.getIconWidth();
}
protected void process(KeyEvent aEvent) {
@@ -443,6 +471,14 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
super.processKeyEvent(e);
}
@Override
protected void processMouseEvent(MouseEvent e) {
if (UIUtil.isActionClick(e, MouseEvent.MOUSE_PRESSED) && isOnNextStepButton(e)) {
e.consume();
}
super.processMouseEvent(e);
}
public Object getData(String dataId) {
if (PlatformDataKeys.SELECTED_ITEM.is(dataId)){
return myList.getSelectedValue();