diff options
Diffstat (limited to 'plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteComponentList.java')
-rw-r--r-- | plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteComponentList.java | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteComponentList.java b/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteComponentList.java new file mode 100644 index 000000000000..77b58a922a87 --- /dev/null +++ b/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteComponentList.java @@ -0,0 +1,403 @@ +/* + * 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.ide.palette.impl; + +import com.intellij.ide.dnd.*; +import com.intellij.ide.palette.PaletteGroup; +import com.intellij.ide.palette.PaletteItem; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.ActionPlaces; +import com.intellij.openapi.actionSystem.ActionPopupMenu; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.PopupHandler; +import com.intellij.ui.components.JBList; +import com.intellij.util.ui.PlatformColors; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.plaf.basic.BasicListUI; +import java.awt.*; +import java.awt.event.*; + +/** + * @author yole + */ +public class PaletteComponentList extends JBList { + private final Project myProject; + private final PaletteWindow myPalette; + private final PaletteGroup myGroup; + private int myHoverIndex = -1; + private int myBeforeClickSelectedRow = -1; + private int myDropTargetIndex = -1; + private boolean myNeedClearSelection = false; + + public PaletteComponentList(Project project, PaletteWindow palette, PaletteGroup group) { + myProject = project; + myPalette = palette; + myGroup = group; + setModel(new AbstractListModel() { + public int getSize() { + return myGroup.getItems().length; + } + + public Object getElementAt(int index) { + return myGroup.getItems() [index]; + } + }); + + addMouseListener(new MouseAdapter() { + @Override public void mouseEntered(MouseEvent e) { + setHoverIndex(locationToIndex(e.getPoint())); + } + + @Override public void mouseExited(MouseEvent e) { + setHoverIndex(-1); + } + + @Override public void mousePressed(MouseEvent e) { + myNeedClearSelection = (SwingUtilities.isLeftMouseButton(e) && + myBeforeClickSelectedRow >= 0 && + locationToIndex(e.getPoint()) == myBeforeClickSelectedRow && + !UIUtil.isControlKeyDown(e) && !e.isShiftDown()); + } + + @Override public void mouseReleased(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e) && + myBeforeClickSelectedRow >= 0 && + locationToIndex(e.getPoint()) == myBeforeClickSelectedRow && + !UIUtil.isControlKeyDown(e) && !e.isShiftDown() && myNeedClearSelection) { + clearSelection(); + } + } + }); + + addMouseListener(new PopupHandler() { + public void invokePopup(final Component comp, final int x, final int y) { + requestFocusInWindow(); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + int index = locationToIndex(new Point(x, y)); + PaletteItem[] items = myGroup.getItems(); + if (index >= 0 && index < items.length) { + if (getSelectedIndex() != index) { + addSelectionInterval(index, index); + } + PaletteItem item = items [index]; + ActionGroup group = item.getPopupActionGroup(); + if (group != null) { + ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group); + popupMenu.getComponent().show(comp, x, y); + } + } + } + }); + } + }); + + addMouseMotionListener(new MouseMotionAdapter() { + public void mouseMoved(MouseEvent e) { + setHoverIndex(locationToIndex(e.getPoint())); + } + }); + + addKeyListener(new KeyListener() { + public void keyPressed(KeyEvent e) { + myPalette.notifyKeyEvent(e); + } + + public void keyReleased(KeyEvent e) { + myPalette.notifyKeyEvent(e); + } + + public void keyTyped(KeyEvent e) { + myPalette.notifyKeyEvent(e); + } + }); + + setCellRenderer(new ComponentCellRenderer()); + + setVisibleRowCount(0); + setLayoutOrientation(HORIZONTAL_WRAP); + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + final DnDManager dndManager = DnDManager.getInstance(); + dndManager.registerSource(new MyDnDSource(), this); + dndManager.registerTarget(new MyDnDTarget(), this); + + initActions(); + } + + private void setHoverIndex(final int index) { + if (index != myHoverIndex) { + if (myHoverIndex >= 0) repaint(getCellBounds(myHoverIndex, myHoverIndex)); + myHoverIndex = index; + if (myHoverIndex >= 0) repaint(getCellBounds(myHoverIndex, myHoverIndex)); + } + } + + private void setDropTargetIndex(final int index) { + if (index != myDropTargetIndex) { + myDropTargetIndex = index; + repaint(); + } + } + + @Override public void updateUI() { + setUI(new ComponentListUI()); + invalidate(); + } + + private void initActions() { + @NonNls ActionMap map = getActionMap(); + map.put( "selectPreviousRow", new MoveFocusAction( map.get( "selectPreviousRow" ), false ) ); + map.put( "selectNextRow", new MoveFocusAction( map.get( "selectNextRow" ), true ) ); + map.put( "selectPreviousColumn", new MoveFocusAction( new ChangeColumnAction( map.get( "selectPreviousColumn" ), false ), false ) ); + map.put( "selectNextColumn", new MoveFocusAction( new ChangeColumnAction( map.get( "selectNextColumn" ), true ), true ) ); + } + + Integer myTempWidth; + + public int getWidth () { + return (myTempWidth == null) ? super.getWidth () : myTempWidth.intValue (); + } + + public int getPreferredHeight(final int width) { + myTempWidth = width; + try { + return getUI().getPreferredSize(this).height; + } + finally { + myTempWidth = null; + } + } + + public void takeFocusFrom(PaletteGroupHeader paletteGroup, int indexToSelect) { + if (indexToSelect == -1) { + //this is not 'our' CategoryButton so we'll assume it's the one below this category list + indexToSelect = getModel().getSize() - 1; + } + else if (getModel().getSize() == 0) { + indexToSelect = -1; + } + requestFocus(); + setSelectedIndex(indexToSelect); + if (indexToSelect >= 0) { + ensureIndexIsVisible(indexToSelect); + } + } + + @Override protected void paintComponent(Graphics g) { + super.paintComponent(g); + if (myDropTargetIndex >= 0) { + int dropLineY; + Rectangle rc; + if (myDropTargetIndex == myGroup.getItems().length) { + rc = getCellBounds(myDropTargetIndex-1, myDropTargetIndex-1); + dropLineY = (int)rc.getMaxY()-1; + } + else { + rc = getCellBounds(myDropTargetIndex, myDropTargetIndex); + dropLineY = rc.y; + } + Graphics2D g2d = (Graphics2D) g; + g2d.setColor(PlatformColors.BLUE); + g2d.setStroke(new BasicStroke(2.0f)); + g2d.drawLine(rc.x, dropLineY, rc.x+rc.width, dropLineY); + g2d.drawLine(rc.x, dropLineY-2, rc.x, dropLineY+2); + g2d.drawLine(rc.x+rc.width, dropLineY-2, rc.x+rc.width, dropLineY+2); + } + } + + class ComponentListUI extends BasicListUI { + private ComponentListListener myListener; + + @Override protected void updateLayoutState() { + super.updateLayoutState(); + + if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) { + Insets insets = list.getInsets(); + int listWidth = list.getWidth() - (insets.left + insets.right); + if (listWidth >= cellWidth) { + int columnCount = listWidth / cellWidth; + cellWidth = (columnCount == 0) ? 1 : listWidth / columnCount; + } + } + } + + @Override protected void installListeners() { + myListener = new ComponentListListener(); + addMouseListener(myListener); + super.installListeners(); + } + + @Override + protected void uninstallListeners() { + if (myListener != null) { + removeMouseListener(myListener); + } + super.uninstallListeners(); + } + + private class ComponentListListener extends MouseAdapter { + @Override public void mousePressed(MouseEvent e) { + myBeforeClickSelectedRow = list.getSelectedIndex(); + } + } + } + + private static class ComponentCellRenderer extends ColoredListCellRenderer { + protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { + PaletteItem paletteItem = (PaletteItem) value; + clear(); + paletteItem.customizeCellRenderer(this, selected, hasFocus); + } + } + + private class MoveFocusAction extends AbstractAction { + private final Action defaultAction; + private final boolean focusNext; + + public MoveFocusAction(Action defaultAction, boolean focusNext) { + this.defaultAction = defaultAction; + this.focusNext = focusNext; + } + + public void actionPerformed(ActionEvent e) { + int selIndexBefore = getSelectedIndex(); + defaultAction.actionPerformed(e); + int selIndexCurrent = getSelectedIndex(); + if (selIndexBefore != selIndexCurrent) return; + + if (focusNext && 0 == selIndexCurrent) return; + + KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + Container container = kfm.getCurrentFocusCycleRoot(); + FocusTraversalPolicy policy = container.getFocusTraversalPolicy(); + if (null == policy) policy = kfm.getDefaultFocusTraversalPolicy(); + Component next = focusNext + ? policy.getComponentAfter(container, PaletteComponentList.this) + : policy.getComponentBefore(container, PaletteComponentList.this); + if (null != next && next instanceof PaletteGroupHeader) { + clearSelection(); + next.requestFocus(); + ((PaletteGroupHeader)next).scrollRectToVisible(next.getBounds()); + } + } + } + + private class ChangeColumnAction extends AbstractAction { + private final Action defaultAction; + private final boolean selectNext; + + public ChangeColumnAction(Action defaultAction, boolean selectNext) { + this.defaultAction = defaultAction; + this.selectNext = selectNext; + } + + public void actionPerformed(ActionEvent e) { + int selIndexBefore = getSelectedIndex(); + defaultAction.actionPerformed(e); + int selIndexCurrent = getSelectedIndex(); + if ((selectNext && selIndexBefore < selIndexCurrent) || (!selectNext && selIndexBefore > selIndexCurrent)) return; + + if (selectNext) { + if (selIndexCurrent == selIndexBefore + 1) selIndexCurrent++; + if (selIndexCurrent < getModel().getSize() - 1) { + setSelectedIndex(selIndexCurrent + 1); + scrollRectToVisible(getCellBounds(selIndexCurrent + 1, selIndexCurrent + 1)); + } + } + else { + if (selIndexCurrent > 0) { + setSelectedIndex(selIndexCurrent - 1); + scrollRectToVisible(getCellBounds(selIndexCurrent - 1, selIndexCurrent - 1)); + } + } + } + } + + private class MyDnDTarget implements DnDTarget { + + public boolean update(DnDEvent aEvent) { + setHoverIndex(-1); + if (aEvent.getAttachedObject() instanceof PaletteItem) { + setDropTargetIndex(locationToTargetIndex(aEvent.getPoint())); + aEvent.setDropPossible(true); + } + else { + setDropTargetIndex(-1); + aEvent.setDropPossible(false); + } + return false; + } + + public void drop(DnDEvent aEvent) { + setDropTargetIndex(-1); + if (aEvent.getAttachedObject() instanceof PaletteItem) { + int index = locationToTargetIndex(aEvent.getPoint()); + if (index >= 0) { + myGroup.handleDrop(myProject, (PaletteItem) aEvent.getAttachedObject(), index); + } + } + } + + public void cleanUpOnLeave() { + setDropTargetIndex(-1); + } + + private int locationToTargetIndex(Point location) { + int row = locationToIndex(location); + if (row < 0) { + return -1; + } + Rectangle rc = getCellBounds(row, row); + return location.y < rc.getCenterY() ? row : row + 1; + } + + public void updateDraggedImage(Image image, Point dropPoint, Point imageOffset) { + } + } + + private class MyDnDSource implements DnDSource { + public boolean canStartDragging(DnDAction action, Point dragOrigin) { + int index = locationToIndex(dragOrigin); + return index >= 0 && myGroup.getItems() [index].startDragging() != null; + } + + public DnDDragStartBean startDragging(DnDAction action, Point dragOrigin) { + int index = locationToIndex(dragOrigin); + if (index < 0) return null; + return myGroup.getItems() [index].startDragging(); + } + + @Nullable + public Pair<Image, Point> createDraggedImage(DnDAction action, Point dragOrigin) { + return null; + } + + public void dragDropEnd() { + } + + public void dropActionChanged(final int gestureModifiers) { + myPalette.notifyDropActionChanged(gestureModifiers); + } + } +} |