summaryrefslogtreecommitdiff
path: root/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteComponentList.java
diff options
context:
space:
mode:
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.java403
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);
+ }
+ }
+}