/* * Copyright 2000-2017 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.designer.palette; import com.intellij.designer.componentTree.TreeTransfer; import com.intellij.designer.designSurface.DesignerEditorPanel; import com.intellij.ide.dnd.DnDAction; import com.intellij.ide.dnd.DnDDragStartBean; import com.intellij.ide.dnd.DnDManager; import com.intellij.ide.dnd.DnDSource; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBList; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.plaf.basic.BasicListUI; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; /** * @author Alexander Lobas */ public class PaletteItemsComponent extends JBList { private static final SimpleTextAttributes DEPRECATED_ATTRIBUTES = new SimpleTextAttributes(SimpleTextAttributes.STYLE_STRIKEOUT, null); private final PaletteGroup myGroup; private final DesignerEditorPanel myDesigner; private int myBeforeClickSelectedRow = -1; private boolean myNeedClearSelection; private Integer myTempWidth; public PaletteItemsComponent(PaletteGroup group, @Nullable DesignerEditorPanel designer) { myGroup = group; myDesigner = designer; setModel(new AbstractListModel() { @Override public int getSize() { return myGroup.getItems().size(); } @Override public Object getElementAt(int index) { return myGroup.getItems().get(index); } }); ColoredListCellRenderer renderer = new ColoredListCellRenderer() { @Override protected void customizeCellRenderer(@NotNull JList list, Object value, int index, boolean selected, boolean hasFocus) { clear(); PaletteItem item = (PaletteItem)value; boolean enabled = item.isEnabled(); setEnabled(enabled); if (enabled) { setIcon(item.getIcon()); } else { setIcon(IconLoader.getDisabledIcon(item.getIcon())); } String title = item.getTitle(); String tooltip = item.getTooltip(); String version = myDesigner.getVersionLabel(item.getVersion()); String deprecatedIn = item.getDeprecatedIn(); boolean deprecated = myDesigner.isDeprecated(deprecatedIn); append(title, deprecated ? DEPRECATED_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES); if (!version.isEmpty()) { version = "<sup><i>" + version + "</i></sup>"; } if (tooltip != null) { String deprecatedMessage = ""; if (deprecated) { deprecatedMessage = String.format("<b>This item is deprecated in version \"%1$s\".<br>", myDesigner.getVersionLabel(deprecatedIn)); String hint = item.getDeprecatedHint(); if (!StringUtil.isEmpty(hint)) { deprecatedMessage += hint; } deprecatedMessage += "</b><br><br>"; } tooltip = "<html><body><center><b>" + StringUtil.escapeXml(title) + "</b>" + version + "</center><p style='width: 300px'>" + deprecatedMessage + tooltip + "</p></body></html>"; } setToolTipText(tooltip); } }; renderer.getIpad().left = 2 * UIUtil.getTreeLeftChildIndent(); renderer.getIpad().right = UIUtil.getTreeRightChildIndent(); setCellRenderer(renderer); setVisibleRowCount(0); setLayoutOrientation(HORIZONTAL_WRAP); setSelectionMode(ListSelectionModel.SINGLE_SELECTION); addMouseListener(new MouseAdapter() { @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(); } } }); setDragEnabled(true); setTransferHandler(new TreeTransfer(PaletteItem.class)); DnDManager.getInstance().registerSource(new DnDSource() { @Override public boolean canStartDragging(DnDAction action, Point dragOrigin) { int index = locationToIndex(dragOrigin); if (index != -1 && myDesigner != null) { PaletteItem paletteItem = myGroup.getItems().get(index); myDesigner.activatePaletteItem(paletteItem); } return false; } @Override public DnDDragStartBean startDragging(DnDAction action, Point dragOrigin) { return null; } @Nullable @Override public Pair<Image, Point> createDraggedImage(DnDAction action, Point dragOrigin) { return null; } @Override public void dragDropEnd() { } @Override public void dropActionChanged(int gestureModifiers) { } }, this); initActions(); } @Override public void updateUI() { setUI(new BasicListUI() { MouseListener myListener; @Override protected void updateLayoutState() { super.updateLayoutState(); 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() { addMouseListener(myListener = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { myBeforeClickSelectedRow = list.getSelectedIndex(); } }); super.installListeners(); } @Override protected void uninstallListeners() { if (myListener != null) { removeMouseListener(myListener); } super.uninstallListeners(); } }); invalidate(); } public int getWidth() { return (myTempWidth == null) ? super.getWidth() : myTempWidth.intValue(); } public int getPreferredHeight(int width) { myTempWidth = width; try { return getUI().getPreferredSize(this).height; } finally { myTempWidth = null; } } public void takeFocusFrom(int indexToSelect) { if (indexToSelect == -1) { indexToSelect = getModel().getSize() - 1; } else if (getModel().getSize() == 0) { indexToSelect = -1; } IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> { IdeFocusManager.getGlobalInstance().requestFocus(this, true); }); setSelectedIndex(indexToSelect); if (indexToSelect >= 0) { ensureIndexIsVisible(indexToSelect); } } public void restoreSelection(PaletteItem paletteItem) { if (paletteItem == null) { clearSelection(); } else { int index = myGroup.getItems().indexOf(paletteItem); if (index == -1) { clearSelection(); } else { takeFocusFrom(index); } } } ////////////////////////////////////////////////////////////////////////////////////////// // // // ////////////////////////////////////////////////////////////////////////////////////////// private void initActions() { 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)); } private class MoveFocusAction extends AbstractAction { private final Action myDefaultAction; private final boolean myFocusNext; public MoveFocusAction(Action defaultAction, boolean focusNext) { myDefaultAction = defaultAction; myFocusNext = focusNext; } public void actionPerformed(ActionEvent e) { int selIndexBefore = getSelectedIndex(); myDefaultAction.actionPerformed(e); int selIndexCurrent = getSelectedIndex(); if (selIndexBefore != selIndexCurrent) { return; } if (myFocusNext && selIndexCurrent == 0) { return; } KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); Container container = kfm.getCurrentFocusCycleRoot(); FocusTraversalPolicy policy = container.getFocusTraversalPolicy(); if (policy == null) { policy = kfm.getDefaultFocusTraversalPolicy(); } Component next = myFocusNext ? policy.getComponentAfter(container, PaletteItemsComponent.this) : policy.getComponentBefore(container, PaletteItemsComponent.this); if (next instanceof PaletteGroupComponent) { clearSelection(); IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> { IdeFocusManager.getGlobalInstance().requestFocus(next, true); }); ((PaletteGroupComponent)next).scrollRectToVisible(next.getBounds()); } } } private class ChangeColumnAction extends AbstractAction { private final Action myDefaultAction; private final boolean mySelectNext; public ChangeColumnAction(Action defaultAction, boolean selectNext) { myDefaultAction = defaultAction; mySelectNext = selectNext; } public void actionPerformed(ActionEvent e) { int selIndexBefore = getSelectedIndex(); myDefaultAction.actionPerformed(e); int selIndexCurrent = getSelectedIndex(); if (mySelectNext && selIndexBefore < selIndexCurrent || !mySelectNext && selIndexBefore > selIndexCurrent) { return; } if (mySelectNext) { 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)); } } } }