diff --git a/platform/lang-impl/src/com/intellij/ui/popup/util/DetailViewImpl.java b/platform/lang-impl/src/com/intellij/ui/popup/util/DetailViewImpl.java index 8d129b4a1c03..b535bc007f0a 100644 --- a/platform/lang-impl/src/com/intellij/ui/popup/util/DetailViewImpl.java +++ b/platform/lang-impl/src/com/intellij/ui/popup/util/DetailViewImpl.java @@ -17,7 +17,6 @@ package com.intellij.ui.popup.util; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.KeyboardShortcut; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; @@ -167,7 +166,7 @@ public class DetailViewImpl extends JPanel implements DetailView, UserDataHolder validate(); getEditor().getScrollingModel().scrollToCaret(ScrollType.CENTER); - getEditor().setBorder(IdeBorderFactory.createBorder(SideBorder.BOTTOM)); + getEditor().setBorder(IdeBorderFactory.createBorder(SideBorder.TOP)); clearHightlighting(); if (lineAttributes != null) { @@ -204,7 +203,7 @@ public class DetailViewImpl extends JPanel implements DetailView, UserDataHolder if (panel != null) { if (myDetailScrollPanel == null) { myDetailPanelWrapper = new JPanel(new GridLayout(1, 1)); - myDetailPanelWrapper.setBorder(BorderFactory.createEmptyBorder(5, 30, 5, 30)); + myDetailPanelWrapper.setBorder(IdeBorderFactory.createEmptyBorder(5, 30, 5, 30)); myDetailPanelWrapper.add(panel); myDetailScrollPanel = @@ -212,7 +211,7 @@ public class DetailViewImpl extends JPanel implements DetailView, UserDataHolder myDetailScrollPanel.setPreferredSize(myDetailPanelWrapper.getPreferredSize()); myDetailScrollPanel.setBorder(null); - add(wrapWithDoneButtonIfNeeded(myDetailScrollPanel), BorderLayout.SOUTH); + add(myDetailScrollPanel, BorderLayout.NORTH); } else { myDetailPanelWrapper.removeAll(); myDetailPanelWrapper.add(panel); diff --git a/platform/lang-impl/src/com/intellij/ui/popup/util/MasterDetailPopupBuilder.java b/platform/lang-impl/src/com/intellij/ui/popup/util/MasterDetailPopupBuilder.java index 9e57a82ff9df..f69bed8cd04e 100644 --- a/platform/lang-impl/src/com/intellij/ui/popup/util/MasterDetailPopupBuilder.java +++ b/platform/lang-impl/src/com/intellij/ui/popup/util/MasterDetailPopupBuilder.java @@ -1,473 +1,517 @@ -/* - * 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.ui.popup.util; - -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.JBPopup; -import com.intellij.openapi.ui.popup.JBPopupListener; -import com.intellij.openapi.ui.popup.LightweightWindowEvent; -import com.intellij.openapi.ui.popup.PopupChooserBuilder; -import com.intellij.openapi.wm.IdeFocusManager; -import com.intellij.ui.*; -import com.intellij.ui.components.JBList; -import com.intellij.ui.speedSearch.FilteringListModel; -import com.intellij.util.Alarm; -import com.intellij.util.Function; -import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; -import java.awt.*; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.io.File; - -public class MasterDetailPopupBuilder { - - private static final Color BORDER_COLOR = Gray._135; - private Project myProject; - private ActionGroup myActions; - private Delegate myDelegate; - private boolean myCloseOnEnter; - - private DetailView myDetailView; - - private JLabel myPathLabel; - - private JBPopup myPopup; - private Alarm myUpdateAlarm; - private JComponent myChooserComponent; - private ActionToolbar myActionToolbar; - private boolean myAddDetailViewToEast = true; - private Dimension myMinSize; - private boolean myCancelOnWindowDeactivation = true; - - - public String getDimensionServiceKey() { - return myDimensionServiceKey; - } - - public void setDimensionServiceKey(String dimensionServiceKey) { - myDimensionServiceKey = dimensionServiceKey; - } - - private String myDimensionServiceKey; - - - public MasterDetailPopupBuilder setDetailView(DetailView detailView) { - myDetailView = detailView; - return this; - } - - public ActionToolbar getActionToolbar() { - return myActionToolbar; - } - - public MasterDetailPopupBuilder(Project project) { - myProject = project; - } - - private String getTitle2Text(String fullText) { - int labelWidth = myPathLabel.getWidth(); - if (fullText == null || fullText.length() == 0) return " "; - while (myPathLabel.getFontMetrics(myPathLabel.getFont()).stringWidth(fullText) > labelWidth) { - int sep = fullText.indexOf(File.separatorChar, 4); - if (sep < 0) return fullText; - fullText = "..." + fullText.substring(sep); - } - - return fullText; - } - - private void doUpdateDetailView() { - final Object[] values = getSelectedItems(); - ItemWrapper wrapper = null; - if (values != null && values.length == 1) { - wrapper = (ItemWrapper)values[0]; - myPathLabel.setText(getTitle2Text(wrapper.footerText())); - } - else { - myPathLabel.setText(" "); - } - final ItemWrapper wrapper1 = wrapper; - myUpdateAlarm.cancelAllRequests(); - myUpdateAlarm.addRequest(new Runnable() { - public void run() { - doUpdateDetailViewWithItem(wrapper1); - } - }, 100); - } - - protected void doUpdateDetailViewWithItem(ItemWrapper wrapper1) { - if (wrapper1 != null) { - wrapper1.updateDetailView(myDetailView); - } - else { - myDetailView.clearEditor(); - } - } - - public JBPopup createMasterDetailPopup() { - - setupRenderer(); - - myPathLabel = new JLabel(" "); - myPathLabel.setHorizontalAlignment(SwingConstants.RIGHT); - - final Font font = myPathLabel.getFont(); - myPathLabel.setFont(font.deriveFont((float)10)); - - myUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); - - if (myDetailView == null) { - myDetailView = new DetailViewImpl(myProject); - } - - JPanel footerPanel = new JPanel(new BorderLayout()) { - @Override - protected void paintComponent(Graphics g) { - super.paintComponent(g); - g.setColor(BORDER_COLOR); - g.drawLine(0, 0, getWidth(), 0); - } - }; - - - Runnable runnable = new Runnable() { - public void run() { - IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(new Runnable() { - public void run() { - Object[] values = getSelectedItems(); - if (values.length == 1) { - myDelegate.itemChosen((ItemWrapper)values[0], myProject, myPopup, false); - } - else { - for (Object value : values) { - if (value instanceof ItemWrapper) { - myDelegate.itemChosen((ItemWrapper)value, myProject, myPopup, false); - } - } - } - } - }); - } - }; - - footerPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); - footerPanel.add(myPathLabel); - - JComponent toolBar = null; - if (myActions != null) { - myActionToolbar = ActionManager.getInstance().createActionToolbar("", myActions, true); - myActionToolbar.setReservePlaceAutoPopupIcon(false); - myActionToolbar.setMinimumButtonSize(new Dimension(20, 20)); - toolBar = myActionToolbar.getComponent(); - toolBar.setOpaque(false); - } - - final PopupChooserBuilder builder = createInnerBuilder(). - setMovable(true). - setResizable(true). - setAutoselectOnMouseMove(false). - setSettingButton(toolBar). - setSouthComponent(footerPanel). - setCancelOnWindowDeactivation(myCancelOnWindowDeactivation); - - - if (myAddDetailViewToEast) { - builder. - setEastComponent((JComponent)myDetailView); - } - - String title = myDelegate.getTitle(); - if (title != null) { - builder.setTitle(title); - } - - - builder. - setItemChoosenCallback(runnable). - setCloseOnEnter(myCloseOnEnter). - setMayBeParent(true). - setDimensionServiceKey(myDimensionServiceKey). - setFilteringEnabled(new Function() { - public String fun(Object o) { - return ((ItemWrapper)o).speedSearchText(); - } - }); - - if (myMinSize != null) { - builder.setMinSize(myMinSize); - } - - myPopup = builder.createPopup(); - builder.getScrollPane().setBorder(IdeBorderFactory.createBorder(SideBorder.RIGHT)); - myPopup.addListener(new JBPopupListener() { - @Override - public void beforeShown(LightweightWindowEvent event) { - //To change body of implemented methods use File | Settings | File Templates. - } - - @Override - public void onClosed(LightweightWindowEvent event) { - myDetailView.clearEditor(); - } - }); - return myPopup; - } - - private void setupRenderer() { - if (myChooserComponent instanceof JList) { - final JList list = (JList)myChooserComponent; - list.setCellRenderer(new ListItemRenderer(myDelegate, myProject)); - } - } - - private PopupChooserBuilder createInnerBuilder() { - if (myChooserComponent instanceof JList) { - return new PopupChooserBuilder((JList)myChooserComponent); - } - else if (myChooserComponent instanceof JTree) { - return new PopupChooserBuilder((JTree)myChooserComponent); - } - return null; - } - - public ItemWrapper[] getSelectedItems() { - Object[] values = new Object[0]; - if (myChooserComponent instanceof JList) { - values = ((JList)myChooserComponent).getSelectedValues(); - - } - else if (myChooserComponent instanceof JTree) { - values = myDelegate.getSelectedItemsInTree(); - } - ItemWrapper[] items = new ItemWrapper[values.length]; - for (int i = 0; i < values.length; i++) { - items[i] = (ItemWrapper)values[i]; - } - return items; - } - - private void updateDetailViewLater() { - //noinspection SSBasedInspection - //SwingUtilities.invokeLater(new Runnable() { - // public void run() { - // doUpdateDetailView(); - // } - //}); - doUpdateDetailView(); - } - - public void setAddDetailViewToEast(boolean addDetailViewToEast) { - myAddDetailViewToEast = addDetailViewToEast; - } - - public MasterDetailPopupBuilder setMinSize(Dimension minSize) { - myMinSize = minSize; - return this; - } - - public MasterDetailPopupBuilder setCancelOnWindowDeactivation(boolean cancelOnWindowDeactivation) { - myCancelOnWindowDeactivation = cancelOnWindowDeactivation; - return this; - } - - public static boolean allowedToRemoveItems(Object[] values) { - for (Object value : values) { - ItemWrapper item = (ItemWrapper)value; - if (!item.allowedToRemove()) { - return false; - } - } - return values.length > 0; - } - - public void removeSelectedItems(Project project) { - if (myChooserComponent instanceof JList) { - final JList list = (JList)myChooserComponent; - int index = list.getSelectedIndex(); - if (index == -1 || index >= list.getModel().getSize()) { - return; - } - Object[] values = list.getSelectedValues(); - for (Object value : values) { - ItemWrapper item = (ItemWrapper)value; - - DefaultListModel model = list.getModel() instanceof DefaultListModel - ? (DefaultListModel)list.getModel() - : (DefaultListModel)((FilteringListModel)list.getModel()).getOriginalModel(); - if (item.allowedToRemove()) { - model.removeElement(item); - - if (model.getSize() > 0) { - if (model.getSize() == index) { - list.setSelectedIndex(model.getSize() - 1); - } - else if (model.getSize() > index) { - list.setSelectedIndex(index); - } - } - else { - list.clearSelection(); - } - item.removed(project); - } - } - } - else { - final Object[] items = getSelectedItems(); - for (Object item : items) { - ((ItemWrapper)item).removed(project); - } - } - } - - public MasterDetailPopupBuilder setActionsGroup(@Nullable ActionGroup actions) { - myActions = actions; - return this; - } - - public MasterDetailPopupBuilder setTree(final JTree tree) { - setChooser(tree); - - tree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { - @Override - public void valueChanged(TreeSelectionEvent event) { - updateDetailViewLater(); - } - }); - - return this; - } - - public MasterDetailPopupBuilder setList(final JBList list) { - setChooser(list); - final ListSelectionModel listSelectionModel = list.getSelectionModel(); - listSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - - listSelectionModel.addListSelectionListener(new ListSelectionListener() { - public void valueChanged(final ListSelectionEvent e) { - updateDetailViewLater(); - } - }); - - - if (list.getModel().getSize() == 0) { - list.clearSelection(); - } - return this; - } - - private void setChooser(JComponent list) { - myChooserComponent = list; - list.addKeyListener(new KeyAdapter() { - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_DELETE) { - removeSelectedItems(myProject); - } - else if (e.getModifiersEx() == 0) { - myDelegate.handleMnemonic(e, myProject, myPopup); - } - } - }); - new AnAction(){ - @Override - public void actionPerformed(AnActionEvent e) { - ItemWrapper[] items = getSelectedItems(); - if (items.length > 0) { - myDelegate.itemChosen(items[0], myProject, myPopup, true); - } - } - }.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)), list); - - } - - public MasterDetailPopupBuilder setDelegate(Delegate delegate) { - myDelegate = delegate; - return this; - } - - public MasterDetailPopupBuilder setCloseOnEnter(boolean closeOnEnter) { - myCloseOnEnter = closeOnEnter; - return this; - } - - public interface Delegate { - @Nullable - String getTitle(); - - void handleMnemonic(KeyEvent e, Project project, JBPopup popup); - - @Nullable - JComponent createAccessoryView(Project project); - - Object[] getSelectedItemsInTree(); - - void itemChosen(ItemWrapper item, Project project, JBPopup popup, boolean withEnterOrDoubleClick); - } - - public static class ListItemRenderer extends JPanel implements ListCellRenderer { - private final Project myProject; - private final ColoredListCellRenderer myRenderer; - private Delegate myDelegate; - - private ListItemRenderer(Delegate delegate, Project project) { - super(new BorderLayout()); - myProject = project; - setBackground(UIUtil.getListBackground()); - this.myDelegate = delegate; - final JComponent accessory = myDelegate.createAccessoryView(project); - - if (accessory != null) { - add(accessory, BorderLayout.WEST); - } - - myRenderer = new ColoredListCellRenderer() { - @Override - protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { - if (value instanceof ItemWrapper) { - final ItemWrapper wrapper = (ItemWrapper)value; - wrapper.setupRenderer(this, myProject, selected); - if (accessory != null) { - wrapper.updateAccessoryView(accessory); - } - } - } - }; - add(myRenderer, BorderLayout.CENTER); - } - - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - if (value instanceof SplitterItem) { - String label = ((SplitterItem)value).getText(); - final TitledSeparator separator = new TitledSeparator(label); - separator.setBackground(UIUtil.getListBackground()); - separator.setForeground(UIUtil.getListForeground()); - return separator; - } - myRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - myRenderer.revalidate(); - - return this; - } - } -} +/* + * 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.ui.popup.util; + +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupListener; +import com.intellij.openapi.ui.popup.LightweightWindowEvent; +import com.intellij.openapi.ui.popup.PopupChooserBuilder; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.ui.*; +import com.intellij.ui.components.JBList; +import com.intellij.ui.speedSearch.FilteringListModel; +import com.intellij.util.Alarm; +import com.intellij.util.Function; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import java.awt.*; +import java.awt.event.*; +import java.io.File; + +public class MasterDetailPopupBuilder { + + private static final Color BORDER_COLOR = Gray._135; + private Project myProject; + private ActionGroup myActions; + private Delegate myDelegate; + private boolean myCloseOnEnter; + + private DetailView myDetailView; + + private JLabel myPathLabel; + + private JBPopup myPopup; + private Alarm myUpdateAlarm; + private JComponent myChooserComponent; + private ActionToolbar myActionToolbar; + private boolean myAddDetailViewToEast = true; + private Dimension myMinSize; + private boolean myCancelOnWindowDeactivation = true; + private Runnable myDoneRunnable; + private boolean myCancelOnClickOutside; + + + public String getDimensionServiceKey() { + return myDimensionServiceKey; + } + + public void setDimensionServiceKey(String dimensionServiceKey) { + myDimensionServiceKey = dimensionServiceKey; + } + + private String myDimensionServiceKey; + + + public MasterDetailPopupBuilder setDetailView(DetailView detailView) { + myDetailView = detailView; + return this; + } + + public ActionToolbar getActionToolbar() { + return myActionToolbar; + } + + public MasterDetailPopupBuilder(Project project) { + myProject = project; + } + + private String getTitle2Text(String fullText) { + int labelWidth = myPathLabel.getWidth(); + if (fullText == null || fullText.length() == 0) return " "; + while (myPathLabel.getFontMetrics(myPathLabel.getFont()).stringWidth(fullText) > labelWidth) { + int sep = fullText.indexOf(File.separatorChar, 4); + if (sep < 0) return fullText; + fullText = "..." + fullText.substring(sep); + } + + return fullText; + } + + private void doUpdateDetailView() { + final Object[] values = getSelectedItems(); + ItemWrapper wrapper = null; + if (values != null && values.length == 1) { + wrapper = (ItemWrapper)values[0]; + myPathLabel.setText(getTitle2Text(wrapper.footerText())); + } + else { + myPathLabel.setText(" "); + } + final ItemWrapper wrapper1 = wrapper; + myUpdateAlarm.cancelAllRequests(); + myUpdateAlarm.addRequest(new Runnable() { + public void run() { + doUpdateDetailViewWithItem(wrapper1); + } + }, 100); + } + + protected void doUpdateDetailViewWithItem(ItemWrapper wrapper1) { + if (wrapper1 != null) { + wrapper1.updateDetailView(myDetailView); + } + else { + myDetailView.clearEditor(); + } + } + + public JBPopup createMasterDetailPopup() { + + setupRenderer(); + + myPathLabel = new JLabel(" "); + myPathLabel.setHorizontalAlignment(SwingConstants.RIGHT); + + final Font font = myPathLabel.getFont(); + myPathLabel.setFont(font.deriveFont((float)10)); + + myUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); + + if (myDetailView == null) { + myDetailView = new DetailViewImpl(myProject); + } + + JPanel footerPanel = new JPanel(new BorderLayout()) { + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + g.setColor(BORDER_COLOR); + g.drawLine(0, 0, getWidth(), 0); + } + }; + + + Runnable runnable = new Runnable() { + public void run() { + IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(new Runnable() { + public void run() { + Object[] values = getSelectedItems(); + if (values.length == 1) { + myDelegate.itemChosen((ItemWrapper)values[0], myProject, myPopup, false); + } + else { + for (Object value : values) { + if (value instanceof ItemWrapper) { + myDelegate.itemChosen((ItemWrapper)value, myProject, myPopup, false); + } + } + } + } + }); + } + }; + + footerPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + footerPanel.add(myPathLabel); + + JComponent toolBar = null; + if (myActions != null) { + myActionToolbar = ActionManager.getInstance().createActionToolbar("", myActions, true); + myActionToolbar.setReservePlaceAutoPopupIcon(false); + myActionToolbar.setMinimumButtonSize(new Dimension(20, 20)); + toolBar = myActionToolbar.getComponent(); + toolBar.setOpaque(false); + } + + + final PopupChooserBuilder builder = createInnerBuilder(). + setMovable(true). + setResizable(true). + setAutoselectOnMouseMove(false). + setSettingButton(toolBar). + setSouthComponent(footerPanel). + setCancelOnWindowDeactivation(myCancelOnWindowDeactivation). + setCancelOnClickOutside(myCancelOnClickOutside); + + + if (myAddDetailViewToEast) { + builder. + setEastComponent((JComponent)myDetailView); + } + + if (myDoneRunnable != null) { + + final JButton done = new JButton("Done"); + done.setMnemonic('o'); + done.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + myDoneRunnable.run(); + } + }); + + + builder.setCommandButton(new ActiveComponent() { + @Override + public void setActive(boolean active) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public JComponent getComponent() { + return done; + } + }); + } + + String title = myDelegate.getTitle(); + if (title != null) { + builder.setTitle(title); + } + + + builder. + setItemChoosenCallback(runnable). + setCloseOnEnter(myCloseOnEnter). + setMayBeParent(true). + setDimensionServiceKey(myDimensionServiceKey). + setFilteringEnabled(new Function() { + public String fun(Object o) { + return ((ItemWrapper)o).speedSearchText(); + } + }); + + if (myMinSize != null) { + builder.setMinSize(myMinSize); + } + + myPopup = builder.createPopup(); + builder.getScrollPane().setBorder(IdeBorderFactory.createBorder(SideBorder.RIGHT)); + myPopup.addListener(new JBPopupListener() { + @Override + public void beforeShown(LightweightWindowEvent event) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void onClosed(LightweightWindowEvent event) { + myDetailView.clearEditor(); + } + }); + + if (myDoneRunnable != null) { + new AnAction("Done") { + @Override + public void actionPerformed(AnActionEvent e) { + myDoneRunnable.run(); + } + }.registerCustomShortcutSet(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK, myPopup.getContent()); + } + + return myPopup; + } + + private void setupRenderer() { + if (myChooserComponent instanceof JList) { + final JList list = (JList)myChooserComponent; + list.setCellRenderer(new ListItemRenderer(myDelegate, myProject)); + } + } + + private PopupChooserBuilder createInnerBuilder() { + if (myChooserComponent instanceof JList) { + return new PopupChooserBuilder((JList)myChooserComponent); + } + else if (myChooserComponent instanceof JTree) { + return new PopupChooserBuilder((JTree)myChooserComponent); + } + return null; + } + + public ItemWrapper[] getSelectedItems() { + Object[] values = new Object[0]; + if (myChooserComponent instanceof JList) { + values = ((JList)myChooserComponent).getSelectedValues(); + } + else if (myChooserComponent instanceof JTree) { + values = myDelegate.getSelectedItemsInTree(); + } + ItemWrapper[] items = new ItemWrapper[values.length]; + for (int i = 0; i < values.length; i++) { + items[i] = (ItemWrapper)values[i]; + } + return items; + } + + private void updateDetailViewLater() { + //noinspection SSBasedInspection + //SwingUtilities.invokeLater(new Runnable() { + // public void run() { + // doUpdateDetailView(); + // } + //}); + doUpdateDetailView(); + } + + public void setAddDetailViewToEast(boolean addDetailViewToEast) { + myAddDetailViewToEast = addDetailViewToEast; + } + + public MasterDetailPopupBuilder setMinSize(Dimension minSize) { + myMinSize = minSize; + return this; + } + + public MasterDetailPopupBuilder setCancelOnWindowDeactivation(boolean cancelOnWindowDeactivation) { + myCancelOnWindowDeactivation = cancelOnWindowDeactivation; + return this; + } + + public void setDoneRunnable(Runnable doneRunnable) { + myDoneRunnable = doneRunnable; + } + + public void setCancelOnClickOutside(boolean cancelOnClickOutside) { + myCancelOnClickOutside = cancelOnClickOutside; + } + + public static boolean allowedToRemoveItems(Object[] values) { + for (Object value : values) { + ItemWrapper item = (ItemWrapper)value; + if (!item.allowedToRemove()) { + return false; + } + } + return values.length > 0; + } + + public void removeSelectedItems(Project project) { + if (myChooserComponent instanceof JList) { + final JList list = (JList)myChooserComponent; + int index = list.getSelectedIndex(); + if (index == -1 || index >= list.getModel().getSize()) { + return; + } + Object[] values = list.getSelectedValues(); + for (Object value : values) { + ItemWrapper item = (ItemWrapper)value; + + DefaultListModel model = list.getModel() instanceof DefaultListModel + ? (DefaultListModel)list.getModel() + : (DefaultListModel)((FilteringListModel)list.getModel()).getOriginalModel(); + if (item.allowedToRemove()) { + model.removeElement(item); + + if (model.getSize() > 0) { + if (model.getSize() == index) { + list.setSelectedIndex(model.getSize() - 1); + } + else if (model.getSize() > index) { + list.setSelectedIndex(index); + } + } + else { + list.clearSelection(); + } + item.removed(project); + } + } + } + else { + final Object[] items = getSelectedItems(); + for (Object item : items) { + ((ItemWrapper)item).removed(project); + } + } + } + + public MasterDetailPopupBuilder setActionsGroup(@Nullable ActionGroup actions) { + myActions = actions; + return this; + } + + public MasterDetailPopupBuilder setTree(final JTree tree) { + setChooser(tree); + + tree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { + @Override + public void valueChanged(TreeSelectionEvent event) { + updateDetailViewLater(); + } + }); + + return this; + } + + public MasterDetailPopupBuilder setList(final JBList list) { + setChooser(list); + final ListSelectionModel listSelectionModel = list.getSelectionModel(); + listSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + listSelectionModel.addListSelectionListener(new ListSelectionListener() { + public void valueChanged(final ListSelectionEvent e) { + updateDetailViewLater(); + } + }); + + + if (list.getModel().getSize() == 0) { + list.clearSelection(); + } + return this; + } + + private void setChooser(JComponent list) { + myChooserComponent = list; + list.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_DELETE) { + removeSelectedItems(myProject); + } + else if (e.getModifiersEx() == 0) { + myDelegate.handleMnemonic(e, myProject, myPopup); + } + } + }); + new AnAction() { + @Override + public void actionPerformed(AnActionEvent e) { + ItemWrapper[] items = getSelectedItems(); + if (items.length > 0) { + myDelegate.itemChosen(items[0], myProject, myPopup, true); + } + } + }.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)), list); + } + + public MasterDetailPopupBuilder setDelegate(Delegate delegate) { + myDelegate = delegate; + return this; + } + + public MasterDetailPopupBuilder setCloseOnEnter(boolean closeOnEnter) { + myCloseOnEnter = closeOnEnter; + return this; + } + + public interface Delegate { + @Nullable + String getTitle(); + + void handleMnemonic(KeyEvent e, Project project, JBPopup popup); + + @Nullable + JComponent createAccessoryView(Project project); + + Object[] getSelectedItemsInTree(); + + void itemChosen(ItemWrapper item, Project project, JBPopup popup, boolean withEnterOrDoubleClick); + } + + public static class ListItemRenderer extends JPanel implements ListCellRenderer { + private final Project myProject; + private final ColoredListCellRenderer myRenderer; + private Delegate myDelegate; + + private ListItemRenderer(Delegate delegate, Project project) { + super(new BorderLayout()); + myProject = project; + setBackground(UIUtil.getListBackground()); + this.myDelegate = delegate; + final JComponent accessory = myDelegate.createAccessoryView(project); + + if (accessory != null) { + add(accessory, BorderLayout.WEST); + } + + myRenderer = new ColoredListCellRenderer() { + @Override + protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { + if (value instanceof ItemWrapper) { + final ItemWrapper wrapper = (ItemWrapper)value; + wrapper.setupRenderer(this, myProject, selected); + if (accessory != null) { + wrapper.updateAccessoryView(accessory); + } + } + } + }; + add(myRenderer, BorderLayout.CENTER); + } + + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + if (value instanceof SplitterItem) { + String label = ((SplitterItem)value).getText(); + final TitledSeparator separator = new TitledSeparator(label); + separator.setBackground(UIUtil.getListBackground()); + separator.setForeground(UIUtil.getListForeground()); + return separator; + } + myRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + myRenderer.revalidate(); + + return this; + } + } +} diff --git a/platform/platform-api/src/com/intellij/openapi/ui/popup/ComponentPopupBuilder.java b/platform/platform-api/src/com/intellij/openapi/ui/popup/ComponentPopupBuilder.java index b0733c86a257..41c6b74eb9e2 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/popup/ComponentPopupBuilder.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/popup/ComponentPopupBuilder.java @@ -20,7 +20,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; -import com.intellij.ui.InplaceButton; +import com.intellij.ui.ActiveComponent; import com.intellij.util.BooleanFunction; import com.intellij.util.Processor; import org.jetbrains.annotations.NonNls; @@ -127,7 +127,7 @@ public interface ComponentPopupBuilder { @NotNull ComponentPopupBuilder setShowShadow(boolean show); @NotNull - ComponentPopupBuilder setCommandButton(@NotNull InplaceButton commandButton); + ComponentPopupBuilder setCommandButton(@NotNull ActiveComponent commandButton); @NotNull ComponentPopupBuilder setCouldPin(@Nullable Processor callback); diff --git a/platform/platform-api/src/com/intellij/openapi/ui/popup/PopupChooserBuilder.java b/platform/platform-api/src/com/intellij/openapi/ui/popup/PopupChooserBuilder.java index e8b8f7440157..64008547fe74 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/popup/PopupChooserBuilder.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/popup/PopupChooserBuilder.java @@ -1,560 +1,566 @@ -/* - * 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.openapi.ui.popup; - -import com.intellij.openapi.actionSystem.DataProvider; -import com.intellij.openapi.actionSystem.PlatformDataKeys; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Pair; -import com.intellij.ui.InplaceButton; -import com.intellij.ui.ListScrollingUtil; -import com.intellij.ui.ScreenUtil; -import com.intellij.ui.ScrollPaneFactory; -import com.intellij.ui.awt.RelativePoint; -import com.intellij.ui.components.JBScrollPane; -import com.intellij.ui.components.JBViewport; -import com.intellij.ui.speedSearch.ListWithFilter; -import com.intellij.ui.treeStructure.treetable.TreeTable; -import com.intellij.util.Function; -import com.intellij.util.ui.UIUtil; -import com.intellij.util.ui.tree.TreeUtil; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.border.Border; -import java.awt.*; -import java.awt.event.*; -import java.util.ArrayList; -import java.util.List; - -/** - * @author max - */ -public class PopupChooserBuilder { - public static final String SELECTED_BY_MOUSE_EVENT = "byMouseEvent"; - - private JComponent myChooserComponent; - private String myTitle; - private final ArrayList myAdditionalKeystrokes = new ArrayList(); - private Runnable myItemChosenRunnable; - private JComponent mySouthComponent; - private JComponent myEastComponent; - - private JBPopup myPopup; - - private boolean myRequestFocus = true; - private boolean myForceResizable = false; - private boolean myForceMovable = false; - private String myDimensionServiceKey = null; - private Computable myCancelCallback; - private boolean myAutoselect = true; - private float myAlpha; - private Component[] myFocusOwners = new Component[0]; - private boolean myCancelKeyEnabled = true; - - ArrayList myListeners = new ArrayList(); - private String myAd; - private Dimension myMinSize; - private InplaceButton myCommandButton; - private final List> myKeyboardActions = new ArrayList>(); - private Component mySettingsButtons; - private boolean myAutoselectOnMouseMove = true; - - private Function myItemsNamer = null; - private boolean myMayBeParent; - private int myAdAlignment = SwingUtilities.LEFT; - private boolean myModalContext; - private boolean myCloseOnEnter = true; - private boolean myCancelOnWindowDeactivation = true; - - - public JScrollPane getScrollPane() { - return myScrollPane; - } - - private JScrollPane myScrollPane; - - public PopupChooserBuilder(@NotNull JList list) { - myChooserComponent = list; - } - - public PopupChooserBuilder(@NotNull JTable table) { - myChooserComponent = table; - } - - public PopupChooserBuilder(@NotNull JTree tree) { - myChooserComponent = tree; - } - - @NotNull - public PopupChooserBuilder setTitle(@NotNull @Nls String title) { - myTitle = title; - return this; - } - - @NotNull - public PopupChooserBuilder addAdditionalChooseKeystroke(@Nullable KeyStroke keyStroke) { - if (keyStroke != null) { - myAdditionalKeystrokes.add(keyStroke); - } - return this; - } - - @NotNull - public PopupChooserBuilder setItemChoosenCallback(@NotNull Runnable runnable) { - myItemChosenRunnable = runnable; - return this; - } - - @NotNull - public PopupChooserBuilder setSouthComponent(@NotNull JComponent cmp) { - mySouthComponent = cmp; - return this; - } - - @NotNull - public PopupChooserBuilder setEastComponent(@NotNull JComponent cmp) { - myEastComponent = cmp; - return this; - } - - - public PopupChooserBuilder setRequestFocus(final boolean requestFocus) { - myRequestFocus = requestFocus; - return this; - } - - public PopupChooserBuilder setResizable(final boolean forceResizable) { - myForceResizable = forceResizable; - return this; - } - - - public PopupChooserBuilder setMovable(final boolean forceMovable) { - myForceMovable = forceMovable; - return this; - } - - public PopupChooserBuilder setDimensionServiceKey(@NonNls String key){ - myDimensionServiceKey = key; - return this; - } - - public PopupChooserBuilder setCancelCallback(Computable callback) { - myCancelCallback = callback; - return this; - } - - public PopupChooserBuilder setCommandButton(@NotNull InplaceButton commandButton) { - myCommandButton = commandButton; - return this; - } - - public PopupChooserBuilder setAlpha(final float alpha) { - myAlpha = alpha; - return this; - } - - public PopupChooserBuilder setAutoselectOnMouseMove(final boolean doAutoSelect) { - myAutoselectOnMouseMove = doAutoSelect; - return this; - } - - public PopupChooserBuilder setFilteringEnabled(Function namer) { - myItemsNamer = namer; - return this; - } - - public PopupChooserBuilder setModalContext(boolean modalContext) { - myModalContext = modalContext; - return this; - } - - @NotNull - public JBPopup createPopup() { - JList list = null; - if (myChooserComponent instanceof JList) { - list = (JList)myChooserComponent; - myChooserComponent = ListWithFilter.wrap(list, new MyListWrapper(list), myItemsNamer); - } - - JPanel contentPane = new JPanel(new BorderLayout()); - if (!myForceMovable && myTitle != null) { - JLabel label = new JLabel(myTitle); - label.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 5)); - label.setHorizontalAlignment(SwingConstants.CENTER); - contentPane.add(label, BorderLayout.NORTH); - } - - if (list != null) { - if (list.getSelectedIndex() == -1 && myAutoselect) { - list.setSelectedIndex(0); - } - } - - - (list != null ? list : myChooserComponent).addMouseListener(new MouseAdapter() { - @Override - public void mouseReleased(MouseEvent e) { - if (UIUtil.isActionClick(e, MouseEvent.MOUSE_RELEASED) && !UIUtil.isSelectionButtonDown(e) && !e.isConsumed()) { - if (myCloseOnEnter) { - closePopup(true, e, true); - } - else { - myItemChosenRunnable.run(); - } - } - } - }); - - registerClosePopupKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), false); - if (myCloseOnEnter) { - registerClosePopupKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), true); - } - else { - registerKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - myItemChosenRunnable.run(); - } - }); - } - for (KeyStroke keystroke : myAdditionalKeystrokes) { - registerClosePopupKeyboardAction(keystroke, true); - } - - if (myChooserComponent instanceof ListWithFilter) { - myScrollPane = ((ListWithFilter)myChooserComponent).getScrollPane(); - } - else if (myChooserComponent instanceof JTable) { - myScrollPane = createScrollPane((JTable)myChooserComponent); - } - else if (myChooserComponent instanceof JTree) { - myScrollPane = createScrollPane((JTree)myChooserComponent); - } - else { - throw new IllegalStateException("PopupChooserBuilder is intended to be constructed with one of JTable, JTree, JList components"); - } - - myScrollPane.getViewport().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - Insets viewportPadding = UIUtil.getListViewportPadding(); - ((JComponent)myScrollPane.getViewport().getView()).setBorder(BorderFactory.createEmptyBorder(viewportPadding.top, viewportPadding.left, viewportPadding.bottom, viewportPadding.right)); - - if (myChooserComponent instanceof ListWithFilter) { - contentPane.add(myChooserComponent, BorderLayout.CENTER); - } - else { - contentPane.add(myScrollPane, BorderLayout.CENTER); - } - - if (mySouthComponent != null) { - contentPane.add(mySouthComponent, BorderLayout.SOUTH); - } - - if (myEastComponent != null) { - contentPane.add(myEastComponent, BorderLayout.EAST); - } - - ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(contentPane, myChooserComponent); - for (JBPopupListener each : myListeners) { - builder.addListener(each); - } - - builder.setDimensionServiceKey(null, myDimensionServiceKey, false) - .setRequestFocus(myRequestFocus) - .setResizable(myForceResizable) - .setMovable(myForceMovable) - .setTitle(myForceMovable ? myTitle : null) - .setCancelCallback(myCancelCallback) - .setAlpha(myAlpha) - .setFocusOwners(myFocusOwners) - .setCancelKeyEnabled(myCancelKeyEnabled && !(myChooserComponent instanceof ListWithFilter)) - .setAdText(myAd, myAdAlignment) - .setKeyboardActions(myKeyboardActions) - .setMayBeParent(myMayBeParent) - .setLocateWithinScreenBounds(true) - .setCancelOnOtherWindowOpen(true) - .setModalContext(myModalContext) - .setCancelOnWindowDeactivation(myCancelOnWindowDeactivation); - - if (myCommandButton != null) { - builder.setCommandButton(myCommandButton); - } - - if (myMinSize != null) { - builder.setMinSize(myMinSize); - } - if (mySettingsButtons != null) { - builder.setSettingButtons(mySettingsButtons); - } - myPopup = builder.createPopup(); - return myPopup; - } - - public PopupChooserBuilder setMinSize(final Dimension dimension) { - myMinSize = dimension; - return this; - } - - public PopupChooserBuilder registerKeyboardAction(KeyStroke keyStroke, ActionListener actionListener) { - myKeyboardActions.add(Pair.create(actionListener, keyStroke)); - return this; - } - - private void registerClosePopupKeyboardAction(final KeyStroke keyStroke, final boolean shouldPerformAction) { - registerPopupKeyboardAction(keyStroke, new AbstractAction() { - public void actionPerformed(ActionEvent e) { - if (!shouldPerformAction && myChooserComponent instanceof ListWithFilter) { - if (((ListWithFilter)myChooserComponent).resetFilter()) return; - } - closePopup(shouldPerformAction, null, shouldPerformAction); - } - }); - } - - private void registerPopupKeyboardAction(final KeyStroke keyStroke, AbstractAction action) { - myChooserComponent.registerKeyboardAction(action, keyStroke, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); - } - - private void closePopup(boolean shouldPerformAction, MouseEvent e, boolean isOk) { - if (shouldPerformAction) { - myPopup.setFinalRunnable(myItemChosenRunnable); - } - - if (isOk) { - myPopup.closeOk(e); - } else { - myPopup.cancel(e); - } - } - - @NotNull - private JScrollPane createScrollPane(final JTable table) { - if (table instanceof TreeTable) { - TreeUtil.expandAll(((TreeTable)table).getTree()); - } - - JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(table); - - scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - - if (table.getSelectedRow() == -1) { - table.getSelectionModel().setSelectionInterval(0, 0); - } - - if (table.getRowCount() >= 20) { - scrollPane.getViewport().setPreferredSize(new Dimension(table.getPreferredScrollableViewportSize().width, 300)); - } - else { - scrollPane.getViewport().setPreferredSize(table.getPreferredSize()); - } - - if (myAutoselectOnMouseMove) { - table.addMouseMotionListener(new MouseMotionAdapter() { - boolean myIsEngaged = false; - public void mouseMoved(MouseEvent e) { - if (myIsEngaged) { - int index = table.rowAtPoint(e.getPoint()); - table.getSelectionModel().setSelectionInterval(index, index); - } - else { - myIsEngaged = true; - } - } - }); - } - - return scrollPane; - } - - @NotNull - private JScrollPane createScrollPane(final JTree tree) { - TreeUtil.expandAll(tree); - - JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(tree); - - scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - - if (tree.getSelectionCount() == 0) { - tree.setSelectionRow(0); - } - - if (tree.getRowCount() >= 20) { - scrollPane.getViewport().setPreferredSize(new Dimension(tree.getPreferredScrollableViewportSize().width, 300)); - } - else { - scrollPane.getViewport().setPreferredSize(tree.getPreferredSize()); - } - - if (myAutoselectOnMouseMove) { - tree.addMouseMotionListener(new MouseMotionAdapter() { - boolean myIsEngaged = false; - public void mouseMoved(MouseEvent e) { - if (myIsEngaged) { - final Point p = e.getPoint(); - int index = tree.getRowForLocation(p.x, p.y); - tree.setSelectionRow(index); - } - else { - myIsEngaged = true; - } - } - }); - } - - return scrollPane; - } - - public PopupChooserBuilder setAutoSelectIfEmpty(final boolean autoselect) { - myAutoselect = autoselect; - return this; - } - - public PopupChooserBuilder setCancelKeyEnabled(final boolean enabled) { - myCancelKeyEnabled = enabled; - return this; - } - - public PopupChooserBuilder addListener(final JBPopupListener listener) { - myListeners.add(listener); - return this; - } - - public PopupChooserBuilder setSettingButton(Component abutton) { - mySettingsButtons = abutton; - return this; - } - - public PopupChooserBuilder setMayBeParent(boolean mayBeParent) { - myMayBeParent = mayBeParent; - return this; - } - - public PopupChooserBuilder setCloseOnEnter(boolean closeOnEnter) { - myCloseOnEnter = closeOnEnter; - return this; - } - - private class MyListWrapper extends JBScrollPane implements DataProvider { - @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"}) - private final JList myList; - - private MyListWrapper(final JList list) { - super(UIUtil.isUnderAquaLookAndFeel() ? 0 : -1); - JBViewport viewport = new JBViewport() { - @Override - protected LayoutManager createLayoutManager() { - return new ViewportLayout() { - @Override - public Dimension preferredLayoutSize(Container parent) { - int size = list.getModel().getSize(); - if (size >= 0 && size <= 20) { - return list.getPreferredSize(); - } else { - final Dimension sz = super.preferredLayoutSize(parent); - final Point p = RelativePoint.getNorthWestOf(myList).getScreenPoint(); - final Rectangle screen = ScreenUtil.getScreenRectangle(p); - - final int bordersEtc = 20; - final int maxWidth = Math.abs(screen.x + screen.width - p.x) - 2 * bordersEtc; - return new Dimension(Math.min(maxWidth, sz.width) + bordersEtc, sz.height + list.getCellBounds(0, 0).height / 2); - } - } - }; - } - }; - list.setVisibleRowCount(15); - setViewport(viewport); - setViewportView(list); - - - if (myAutoselectOnMouseMove) { - list.addMouseMotionListener(new MouseMotionAdapter() { - boolean myIsEngaged = false; - public void mouseMoved(MouseEvent e) { - if (myIsEngaged && !UIUtil.isSelectionButtonDown(e)) { - Point point = e.getPoint(); - int index = list.locationToIndex(point); - list.putClientProperty(SELECTED_BY_MOUSE_EVENT, Boolean.TRUE); - list.setSelectedIndex(index); - list.putClientProperty(SELECTED_BY_MOUSE_EVENT, Boolean.FALSE); - } - else { - myIsEngaged = true; - } - } - }); - } - - ListScrollingUtil.installActions(list); - - setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - myList = list; - } - - @Nullable - public Object getData(@NonNls String dataId) { - if (PlatformDataKeys.SELECTED_ITEM.is(dataId)){ - return myList.getSelectedValue(); - } - if (PlatformDataKeys.SELECTED_ITEMS.is(dataId)){ - return myList.getSelectedValues(); - } - return null; - } - - public void setBorder(Border border) { - if (myList != null){ - myList.setBorder(border); - } - } - - public void requestFocus() { - myList.requestFocus(); - } - - public synchronized void addMouseListener(MouseListener l) { - myList.addMouseListener(l); - } - } - - @NotNull - public PopupChooserBuilder setFocusOwners(@NotNull Component[] focusOwners) { - myFocusOwners = focusOwners; - return this; - } - - @NotNull - public PopupChooserBuilder setAdText(String ad) { - setAdText(ad, SwingUtilities.LEFT); - return this; - } - - public PopupChooserBuilder setAdText(String ad, int alignment) { - myAd = ad; - myAdAlignment = alignment; - return this; - } - - public PopupChooserBuilder setCancelOnWindowDeactivation(boolean cancelOnWindowDeactivation) { - myCancelOnWindowDeactivation = cancelOnWindowDeactivation; - return this; - } -} +/* + * 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.openapi.ui.popup; + +import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Pair; +import com.intellij.ui.*; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.ui.components.JBViewport; +import com.intellij.ui.speedSearch.ListWithFilter; +import com.intellij.ui.treeStructure.treetable.TreeTable; +import com.intellij.util.Function; +import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.tree.TreeUtil; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.border.Border; +import java.awt.*; +import java.awt.event.*; +import java.util.ArrayList; +import java.util.List; + +/** + * @author max + */ +public class PopupChooserBuilder { + public static final String SELECTED_BY_MOUSE_EVENT = "byMouseEvent"; + + private JComponent myChooserComponent; + private String myTitle; + private final ArrayList myAdditionalKeystrokes = new ArrayList(); + private Runnable myItemChosenRunnable; + private JComponent mySouthComponent; + private JComponent myEastComponent; + + private JBPopup myPopup; + + private boolean myRequestFocus = true; + private boolean myForceResizable = false; + private boolean myForceMovable = false; + private String myDimensionServiceKey = null; + private Computable myCancelCallback; + private boolean myAutoselect = true; + private float myAlpha; + private Component[] myFocusOwners = new Component[0]; + private boolean myCancelKeyEnabled = true; + + ArrayList myListeners = new ArrayList(); + private String myAd; + private Dimension myMinSize; + private ActiveComponent myCommandButton; + private final List> myKeyboardActions = new ArrayList>(); + private Component mySettingsButtons; + private boolean myAutoselectOnMouseMove = true; + + private Function myItemsNamer = null; + private boolean myMayBeParent; + private int myAdAlignment = SwingUtilities.LEFT; + private boolean myModalContext; + private boolean myCloseOnEnter = true; + private boolean myCancelOnWindowDeactivation = true; + + public PopupChooserBuilder setCancelOnClickOutside(boolean cancelOnClickOutside) { + myCancelOnClickOutside = cancelOnClickOutside; + return this; + } + + private boolean myCancelOnClickOutside = true; + + + + public JScrollPane getScrollPane() { + return myScrollPane; + } + + private JScrollPane myScrollPane; + + public PopupChooserBuilder(@NotNull JList list) { + myChooserComponent = list; + } + + public PopupChooserBuilder(@NotNull JTable table) { + myChooserComponent = table; + } + + public PopupChooserBuilder(@NotNull JTree tree) { + myChooserComponent = tree; + } + + @NotNull + public PopupChooserBuilder setTitle(@NotNull @Nls String title) { + myTitle = title; + return this; + } + + @NotNull + public PopupChooserBuilder addAdditionalChooseKeystroke(@Nullable KeyStroke keyStroke) { + if (keyStroke != null) { + myAdditionalKeystrokes.add(keyStroke); + } + return this; + } + + @NotNull + public PopupChooserBuilder setItemChoosenCallback(@NotNull Runnable runnable) { + myItemChosenRunnable = runnable; + return this; + } + + @NotNull + public PopupChooserBuilder setSouthComponent(@NotNull JComponent cmp) { + mySouthComponent = cmp; + return this; + } + + @NotNull + public PopupChooserBuilder setEastComponent(@NotNull JComponent cmp) { + myEastComponent = cmp; + return this; + } + + + public PopupChooserBuilder setRequestFocus(final boolean requestFocus) { + myRequestFocus = requestFocus; + return this; + } + + public PopupChooserBuilder setResizable(final boolean forceResizable) { + myForceResizable = forceResizable; + return this; + } + + + public PopupChooserBuilder setMovable(final boolean forceMovable) { + myForceMovable = forceMovable; + return this; + } + + public PopupChooserBuilder setDimensionServiceKey(@NonNls String key){ + myDimensionServiceKey = key; + return this; + } + + public PopupChooserBuilder setCancelCallback(Computable callback) { + myCancelCallback = callback; + return this; + } + + public PopupChooserBuilder setCommandButton(@NotNull ActiveComponent commandButton) { + myCommandButton = commandButton; + return this; + } + + public PopupChooserBuilder setAlpha(final float alpha) { + myAlpha = alpha; + return this; + } + + public PopupChooserBuilder setAutoselectOnMouseMove(final boolean doAutoSelect) { + myAutoselectOnMouseMove = doAutoSelect; + return this; + } + + public PopupChooserBuilder setFilteringEnabled(Function namer) { + myItemsNamer = namer; + return this; + } + + public PopupChooserBuilder setModalContext(boolean modalContext) { + myModalContext = modalContext; + return this; + } + + @NotNull + public JBPopup createPopup() { + JList list = null; + if (myChooserComponent instanceof JList) { + list = (JList)myChooserComponent; + myChooserComponent = ListWithFilter.wrap(list, new MyListWrapper(list), myItemsNamer); + } + + JPanel contentPane = new JPanel(new BorderLayout()); + if (!myForceMovable && myTitle != null) { + JLabel label = new JLabel(myTitle); + label.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 5)); + label.setHorizontalAlignment(SwingConstants.CENTER); + contentPane.add(label, BorderLayout.NORTH); + } + + if (list != null) { + if (list.getSelectedIndex() == -1 && myAutoselect) { + list.setSelectedIndex(0); + } + } + + + (list != null ? list : myChooserComponent).addMouseListener(new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + if (UIUtil.isActionClick(e, MouseEvent.MOUSE_RELEASED) && !UIUtil.isSelectionButtonDown(e) && !e.isConsumed()) { + if (myCloseOnEnter) { + closePopup(true, e, true); + } + else { + myItemChosenRunnable.run(); + } + } + } + }); + + registerClosePopupKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), false); + if (myCloseOnEnter) { + registerClosePopupKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), true); + } + else { + registerKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + myItemChosenRunnable.run(); + } + }); + } + for (KeyStroke keystroke : myAdditionalKeystrokes) { + registerClosePopupKeyboardAction(keystroke, true); + } + + if (myChooserComponent instanceof ListWithFilter) { + myScrollPane = ((ListWithFilter)myChooserComponent).getScrollPane(); + } + else if (myChooserComponent instanceof JTable) { + myScrollPane = createScrollPane((JTable)myChooserComponent); + } + else if (myChooserComponent instanceof JTree) { + myScrollPane = createScrollPane((JTree)myChooserComponent); + } + else { + throw new IllegalStateException("PopupChooserBuilder is intended to be constructed with one of JTable, JTree, JList components"); + } + + myScrollPane.getViewport().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + Insets viewportPadding = UIUtil.getListViewportPadding(); + ((JComponent)myScrollPane.getViewport().getView()).setBorder(BorderFactory.createEmptyBorder(viewportPadding.top, viewportPadding.left, viewportPadding.bottom, viewportPadding.right)); + + if (myChooserComponent instanceof ListWithFilter) { + contentPane.add(myChooserComponent, BorderLayout.CENTER); + } + else { + contentPane.add(myScrollPane, BorderLayout.CENTER); + } + + if (mySouthComponent != null) { + contentPane.add(mySouthComponent, BorderLayout.SOUTH); + } + + if (myEastComponent != null) { + contentPane.add(myEastComponent, BorderLayout.EAST); + } + + ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(contentPane, myChooserComponent); + for (JBPopupListener each : myListeners) { + builder.addListener(each); + } + + builder.setDimensionServiceKey(null, myDimensionServiceKey, false) + .setRequestFocus(myRequestFocus) + .setResizable(myForceResizable) + .setMovable(myForceMovable) + .setTitle(myForceMovable ? myTitle : null) + .setCancelCallback(myCancelCallback) + .setAlpha(myAlpha) + .setFocusOwners(myFocusOwners) + .setCancelKeyEnabled(myCancelKeyEnabled && !(myChooserComponent instanceof ListWithFilter)) + .setAdText(myAd, myAdAlignment) + .setKeyboardActions(myKeyboardActions) + .setMayBeParent(myMayBeParent) + .setLocateWithinScreenBounds(true) + .setCancelOnOtherWindowOpen(true) + .setModalContext(myModalContext) + .setCancelOnWindowDeactivation(myCancelOnWindowDeactivation) + .setCancelOnClickOutside(myCancelOnClickOutside); + + if (myCommandButton != null) { + builder.setCommandButton(myCommandButton); + } + + if (myMinSize != null) { + builder.setMinSize(myMinSize); + } + if (mySettingsButtons != null) { + builder.setSettingButtons(mySettingsButtons); + } + myPopup = builder.createPopup(); + return myPopup; + } + + public PopupChooserBuilder setMinSize(final Dimension dimension) { + myMinSize = dimension; + return this; + } + + public PopupChooserBuilder registerKeyboardAction(KeyStroke keyStroke, ActionListener actionListener) { + myKeyboardActions.add(Pair.create(actionListener, keyStroke)); + return this; + } + + private void registerClosePopupKeyboardAction(final KeyStroke keyStroke, final boolean shouldPerformAction) { + registerPopupKeyboardAction(keyStroke, new AbstractAction() { + public void actionPerformed(ActionEvent e) { + if (!shouldPerformAction && myChooserComponent instanceof ListWithFilter) { + if (((ListWithFilter)myChooserComponent).resetFilter()) return; + } + closePopup(shouldPerformAction, null, shouldPerformAction); + } + }); + } + + private void registerPopupKeyboardAction(final KeyStroke keyStroke, AbstractAction action) { + myChooserComponent.registerKeyboardAction(action, keyStroke, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + } + + private void closePopup(boolean shouldPerformAction, MouseEvent e, boolean isOk) { + if (shouldPerformAction) { + myPopup.setFinalRunnable(myItemChosenRunnable); + } + + if (isOk) { + myPopup.closeOk(e); + } else { + myPopup.cancel(e); + } + } + + @NotNull + private JScrollPane createScrollPane(final JTable table) { + if (table instanceof TreeTable) { + TreeUtil.expandAll(((TreeTable)table).getTree()); + } + + JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(table); + + scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + if (table.getSelectedRow() == -1) { + table.getSelectionModel().setSelectionInterval(0, 0); + } + + if (table.getRowCount() >= 20) { + scrollPane.getViewport().setPreferredSize(new Dimension(table.getPreferredScrollableViewportSize().width, 300)); + } + else { + scrollPane.getViewport().setPreferredSize(table.getPreferredSize()); + } + + if (myAutoselectOnMouseMove) { + table.addMouseMotionListener(new MouseMotionAdapter() { + boolean myIsEngaged = false; + public void mouseMoved(MouseEvent e) { + if (myIsEngaged) { + int index = table.rowAtPoint(e.getPoint()); + table.getSelectionModel().setSelectionInterval(index, index); + } + else { + myIsEngaged = true; + } + } + }); + } + + return scrollPane; + } + + @NotNull + private JScrollPane createScrollPane(final JTree tree) { + TreeUtil.expandAll(tree); + + JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(tree); + + scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + if (tree.getSelectionCount() == 0) { + tree.setSelectionRow(0); + } + + if (tree.getRowCount() >= 20) { + scrollPane.getViewport().setPreferredSize(new Dimension(tree.getPreferredScrollableViewportSize().width, 300)); + } + else { + scrollPane.getViewport().setPreferredSize(tree.getPreferredSize()); + } + + if (myAutoselectOnMouseMove) { + tree.addMouseMotionListener(new MouseMotionAdapter() { + boolean myIsEngaged = false; + public void mouseMoved(MouseEvent e) { + if (myIsEngaged) { + final Point p = e.getPoint(); + int index = tree.getRowForLocation(p.x, p.y); + tree.setSelectionRow(index); + } + else { + myIsEngaged = true; + } + } + }); + } + + return scrollPane; + } + + public PopupChooserBuilder setAutoSelectIfEmpty(final boolean autoselect) { + myAutoselect = autoselect; + return this; + } + + public PopupChooserBuilder setCancelKeyEnabled(final boolean enabled) { + myCancelKeyEnabled = enabled; + return this; + } + + public PopupChooserBuilder addListener(final JBPopupListener listener) { + myListeners.add(listener); + return this; + } + + public PopupChooserBuilder setSettingButton(Component abutton) { + mySettingsButtons = abutton; + return this; + } + + public PopupChooserBuilder setMayBeParent(boolean mayBeParent) { + myMayBeParent = mayBeParent; + return this; + } + + public PopupChooserBuilder setCloseOnEnter(boolean closeOnEnter) { + myCloseOnEnter = closeOnEnter; + return this; + } + + private class MyListWrapper extends JBScrollPane implements DataProvider { + @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"}) + private final JList myList; + + private MyListWrapper(final JList list) { + super(UIUtil.isUnderAquaLookAndFeel() ? 0 : -1); + JBViewport viewport = new JBViewport() { + @Override + protected LayoutManager createLayoutManager() { + return new ViewportLayout() { + @Override + public Dimension preferredLayoutSize(Container parent) { + int size = list.getModel().getSize(); + if (size >= 0 && size <= 20) { + return list.getPreferredSize(); + } else { + final Dimension sz = super.preferredLayoutSize(parent); + final Point p = RelativePoint.getNorthWestOf(myList).getScreenPoint(); + final Rectangle screen = ScreenUtil.getScreenRectangle(p); + + final int bordersEtc = 20; + final int maxWidth = Math.abs(screen.x + screen.width - p.x) - 2 * bordersEtc; + return new Dimension(Math.min(maxWidth, sz.width) + bordersEtc, sz.height + list.getCellBounds(0, 0).height / 2); + } + } + }; + } + }; + list.setVisibleRowCount(15); + setViewport(viewport); + setViewportView(list); + + + if (myAutoselectOnMouseMove) { + list.addMouseMotionListener(new MouseMotionAdapter() { + boolean myIsEngaged = false; + public void mouseMoved(MouseEvent e) { + if (myIsEngaged && !UIUtil.isSelectionButtonDown(e)) { + Point point = e.getPoint(); + int index = list.locationToIndex(point); + list.putClientProperty(SELECTED_BY_MOUSE_EVENT, Boolean.TRUE); + list.setSelectedIndex(index); + list.putClientProperty(SELECTED_BY_MOUSE_EVENT, Boolean.FALSE); + } + else { + myIsEngaged = true; + } + } + }); + } + + ListScrollingUtil.installActions(list); + + setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + myList = list; + } + + @Nullable + public Object getData(@NonNls String dataId) { + if (PlatformDataKeys.SELECTED_ITEM.is(dataId)){ + return myList.getSelectedValue(); + } + if (PlatformDataKeys.SELECTED_ITEMS.is(dataId)){ + return myList.getSelectedValues(); + } + return null; + } + + public void setBorder(Border border) { + if (myList != null){ + myList.setBorder(border); + } + } + + public void requestFocus() { + myList.requestFocus(); + } + + public synchronized void addMouseListener(MouseListener l) { + myList.addMouseListener(l); + } + } + + @NotNull + public PopupChooserBuilder setFocusOwners(@NotNull Component[] focusOwners) { + myFocusOwners = focusOwners; + return this; + } + + @NotNull + public PopupChooserBuilder setAdText(String ad) { + setAdText(ad, SwingUtilities.LEFT); + return this; + } + + public PopupChooserBuilder setAdText(String ad, int alignment) { + myAd = ad; + myAdAlignment = alignment; + return this; + } + + public PopupChooserBuilder setCancelOnWindowDeactivation(boolean cancelOnWindowDeactivation) { + myCancelOnWindowDeactivation = cancelOnWindowDeactivation; + return this; + } +} diff --git a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java index 9f344c0ccc10..48849f6503d0 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java @@ -176,7 +176,7 @@ public class AbstractPopup implements JBPopup { final boolean cancelOnClickOutside, @Nullable final Set listeners, final boolean useDimServiceForXYLocation, - InplaceButton commandButton, + ActiveComponent commandButton, @Nullable final IconButton cancelButton, @Nullable final MouseChecker cancelOnMouseOutCallback, final boolean cancelOnWindow, diff --git a/platform/platform-impl/src/com/intellij/ui/popup/ComponentPopupBuilderImpl.java b/platform/platform-impl/src/com/intellij/ui/popup/ComponentPopupBuilderImpl.java index 11356428436e..6d27208999cd 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/ComponentPopupBuilderImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/ComponentPopupBuilderImpl.java @@ -23,7 +23,7 @@ import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; -import com.intellij.ui.InplaceButton; +import com.intellij.ui.ActiveComponent; import com.intellij.util.BooleanFunction; import com.intellij.util.Processor; import com.intellij.util.ui.EmptyIcon; @@ -78,7 +78,7 @@ public class ComponentPopupBuilderImpl implements ComponentPopupBuilder { private boolean myShowShadow = true; private boolean myFocusable = true; private boolean myHeaderAlwaysFocusable; - private InplaceButton myCommandButton; + private ActiveComponent myCommandButton; private List> myKeyboardActions = Collections.emptyList(); private Component mySettingsButtons; private boolean myMayBeParent; @@ -171,7 +171,7 @@ public class ComponentPopupBuilderImpl implements ComponentPopupBuilder { return this; } @NotNull - public ComponentPopupBuilder setCommandButton(@NotNull InplaceButton button) { + public ComponentPopupBuilder setCommandButton(@NotNull ActiveComponent button) { myCommandButton = button; return this; } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/tree/BreakpointMasterDetailPopupBuilder.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/tree/BreakpointMasterDetailPopupBuilder.java index 2b443ea9789b..8c9c60bd8e86 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/tree/BreakpointMasterDetailPopupBuilder.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/tree/BreakpointMasterDetailPopupBuilder.java @@ -118,6 +118,7 @@ public class BreakpointMasterDetailPopupBuilder { myPopupBuilder = new MasterDetailPopupBuilder(myProject); if (!myPlainView) { myPopupBuilder.setDimensionServiceKey(getClass().getName()); + myPopupBuilder.setCancelOnClickOutside(false); } myPopupBuilder.setCancelOnWindowDeactivation(false); @@ -212,17 +213,18 @@ public class BreakpointMasterDetailPopupBuilder { myPopupBuilder.setMinSize(new Dimension(-1, 700)); } - myPopup = myPopupBuilder.setCloseOnEnter(false).createMasterDetailPopup(); - - if (view != null) { - view.setDoneRunnable(new Runnable() { + if (!myPlainView) { + myPopupBuilder.setDoneRunnable(new Runnable() { @Override public void run() { myPopup.cancel(); } - }, myPopup.getContent()); + }); } + + myPopup = myPopupBuilder.setCloseOnEnter(false).createMasterDetailPopup(); + myTreeController.setDelegate(new BreakpointItemsTreeController.BreakpointItemsTreeDelegate() { @Override public void execute(BreakpointItem item) {