diff options
Diffstat (limited to 'plugins/ui-designer/src/com/intellij/ide/palette')
9 files changed, 1376 insertions, 0 deletions
diff --git a/plugins/ui-designer/src/com/intellij/ide/palette/PaletteDragEventListener.java b/plugins/ui-designer/src/com/intellij/ide/palette/PaletteDragEventListener.java new file mode 100644 index 000000000000..1a6b681b2a48 --- /dev/null +++ b/plugins/ui-designer/src/com/intellij/ide/palette/PaletteDragEventListener.java @@ -0,0 +1,24 @@ +/* + * 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.ide.palette; + +/** + * @author yole + */ +public interface PaletteDragEventListener { + void dropActionChanged(int gestureModifiers); +} diff --git a/plugins/ui-designer/src/com/intellij/ide/palette/PaletteGroup.java b/plugins/ui-designer/src/com/intellij/ide/palette/PaletteGroup.java new file mode 100644 index 000000000000..a19c1ab8e60d --- /dev/null +++ b/plugins/ui-designer/src/com/intellij/ide/palette/PaletteGroup.java @@ -0,0 +1,65 @@ +/* + * 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.ide.palette; + +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.Nullable; + +/** + * @author yole + */ +public interface PaletteGroup { + PaletteGroup[] EMPTY_ARRAY = new PaletteGroup[0]; + + PaletteItem[] getItems(); + + /** + * Returns the text of the group header for the palette group. + * + * @return the text of the group header for the palette group, or null if no header should be shown. + */ + @Nullable String getName(); + + String getTabName(); + + /** + * Returns the action group from which the context menu is built when the palette + * item is right-clicked. + * + * @return the action group, or null if no context menu should be shown. + */ + @Nullable ActionGroup getPopupActionGroup(); + + /** + * Returns the data for the specified data constant. + * + * @param project the project in the context of which data is requested. + * @param dataId the data constant id (see {@link com.intellij.openapi.actionSystem.PlatformDataKeys}). + * @return the data item, or null if no data is available for this constant. + */ + @Nullable Object getData(Project project, String dataId); + + /** + * Processes the drop of a palette item on the specified index in the palette group. + * + * @param project the project to which the drop target palette belongs. + * @param item the dropped item. + * @param index the index at which the dropped item should be inserted (from 0 to getItems().length). + */ + void handleDrop(Project project, PaletteItem item, int index); +} diff --git a/plugins/ui-designer/src/com/intellij/ide/palette/PaletteItem.java b/plugins/ui-designer/src/com/intellij/ide/palette/PaletteItem.java new file mode 100644 index 000000000000..372839d67266 --- /dev/null +++ b/plugins/ui-designer/src/com/intellij/ide/palette/PaletteItem.java @@ -0,0 +1,56 @@ +/* + * 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.ide.palette; + +import com.intellij.ide.dnd.DnDDragStartBean; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.project.Project; +import com.intellij.ui.ColoredListCellRenderer; +import org.jetbrains.annotations.Nullable; + +/** + * @author yole + */ +public interface PaletteItem { + void customizeCellRenderer(ColoredListCellRenderer cellRenderer, + boolean selected, + boolean hasFocus); + + /** + * Processes dragging the item. + * + * @return the drag start bean for the drag process, or null if the item cannot be dragged. + */ + @Nullable DnDDragStartBean startDragging(); + + /** + * Returns the action group from which the context menu is built when the palette + * item is right-clicked. + * + * @return the action group, or null if no context menu should be shown. + */ + @Nullable ActionGroup getPopupActionGroup(); + + /** + * Returns the data for the specified data constant. + * + * @param project the project in the context of which data is requested. + * @param dataId the data constant id (see {@link com.intellij.openapi.actionSystem.PlatformDataKeys}). + * @return the data item, or null if no data is available for this constant. + */ + @Nullable Object getData(Project project, String dataId); +} diff --git a/plugins/ui-designer/src/com/intellij/ide/palette/PaletteItemProvider.java b/plugins/ui-designer/src/com/intellij/ide/palette/PaletteItemProvider.java new file mode 100644 index 000000000000..3f1b99d36f52 --- /dev/null +++ b/plugins/ui-designer/src/com/intellij/ide/palette/PaletteItemProvider.java @@ -0,0 +1,34 @@ +/* + * 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.ide.palette; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.vfs.VirtualFile; + +import java.beans.PropertyChangeListener; + +/** + * @author yole + */ +public interface PaletteItemProvider { + ExtensionPointName<PaletteItemProvider> EP_NAME = ExtensionPointName.create("com.intellij.paletteItemProvider"); + + PaletteGroup[] getActiveGroups(VirtualFile virtualFile); + + void addListener(PropertyChangeListener listener); + void removeListener(PropertyChangeListener listener); +} 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); + } + } +} diff --git a/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteContentWindow.java b/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteContentWindow.java new file mode 100644 index 000000000000..b5aaccad7b29 --- /dev/null +++ b/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteContentWindow.java @@ -0,0 +1,114 @@ +/* + * 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.ide.palette.impl; + +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; + +/** + * @author yole + */ +public class PaletteContentWindow extends JPanel implements Scrollable { + public PaletteContentWindow() { + setLayout(new PaletteLayoutManager()); + } + + public Dimension getPreferredScrollableViewportSize() { + return getPreferredSize(); + } + + public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { + return 20; + } + + public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { + return 100; + } + + public boolean getScrollableTracksViewportWidth() { + return true; + } + + public boolean getScrollableTracksViewportHeight() { + return false; + } + + @Nullable PaletteGroupHeader getLastGroupHeader() { + PaletteGroupHeader result = null; + for(Component comp: getComponents()) { + if (comp instanceof PaletteGroupHeader) { + result = (PaletteGroupHeader) comp; + } + } + return result; + } + + private static class PaletteLayoutManager implements LayoutManager { + + public void addLayoutComponent(String name, Component comp) { + } + + public void layoutContainer(Container parent) { + int width = parent.getWidth(); + + int height = 0; + for(Component c: parent.getComponents()) { + if (c instanceof PaletteGroupHeader) { + PaletteGroupHeader groupHeader = (PaletteGroupHeader) c; + groupHeader.setLocation(0, height); + if (groupHeader.isVisible()) { + groupHeader.setSize(width, groupHeader.getPreferredSize().height); + height += groupHeader.getPreferredSize().height; + } + else { + groupHeader.setSize(0, 0); + } + if (groupHeader.isSelected() || !groupHeader.isVisible()) { + PaletteComponentList componentList = groupHeader.getComponentList(); + componentList.setSize(width, componentList.getPreferredSize().height); + componentList.setLocation(0, height); + height += componentList.getHeight(); + } + } + } + } + + public Dimension minimumLayoutSize(Container parent) { + return new Dimension(0, 0); + } + + public Dimension preferredLayoutSize(Container parent) { + int height = 0; + int width = parent.getWidth(); + for(Component c: parent.getComponents()) { + if (c instanceof PaletteGroupHeader) { + PaletteGroupHeader groupHeader = (PaletteGroupHeader) c; + height += groupHeader.getHeight(); + if (groupHeader.isSelected()) { + height += groupHeader.getComponentList().getPreferredHeight(width); + } + } + } + return new Dimension(10 /* not used - tracks viewports width*/, height); + } + + public void removeLayoutComponent(Component comp) { + } + } +} diff --git a/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteGroupHeader.java b/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteGroupHeader.java new file mode 100644 index 000000000000..c98caefa3f75 --- /dev/null +++ b/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteGroupHeader.java @@ -0,0 +1,211 @@ +/* + * Copyright 2000-2013 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.DnDEvent; +import com.intellij.ide.dnd.DnDManager; +import com.intellij.ide.dnd.DnDTarget; +import com.intellij.ide.palette.PaletteGroup; +import com.intellij.ide.palette.PaletteItem; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.project.Project; +import com.intellij.ui.PopupHandler; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.border.CompoundBorder; +import java.awt.*; +import java.awt.event.*; + +/** + * @author yole + */ +public class PaletteGroupHeader extends JCheckBox implements DataProvider { + private final PaletteWindow myPaletteWindow; + private PaletteComponentList myComponentList; + private final PaletteGroup myGroup; + + public PaletteGroupHeader(PaletteWindow paletteWindow, PaletteGroup group) { + myPaletteWindow = paletteWindow; + myGroup = group; + if (group.getName() == null) { + setVisible(false); + } + else { + setText(group.getName()); + } + setSelected(true); + addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (myComponentList != null) { + myComponentList.setVisible(isSelected()); + } + } + }); + + addMouseListener(new PopupHandler() { + public void invokePopup(Component comp, int x, int y) { + myPaletteWindow.setLastFocusedGroup(PaletteGroupHeader.this); + showGroupPopupMenu(comp, x, y); + } + }); + + setIcon(UIUtil.getTreeCollapsedIcon()); + setSelectedIcon(UIUtil.getTreeExpandedIcon()); + setFont(getFont().deriveFont(Font.BOLD)); + setFocusPainted(false); + setMargin(new Insets(0, 3, 0, 3)); + setOpaque(true); + if (getBorder() instanceof CompoundBorder) { // from BasicLookAndFeel + Dimension pref = getPreferredSize(); + pref.height -= 3; + setPreferredSize(pref); + } + + DnDManager.getInstance().registerTarget(new DnDTarget() { + public boolean update(DnDEvent aEvent) { + setBorderPainted(true); + aEvent.setDropPossible(aEvent.getAttachedObject() instanceof PaletteItem); + return true; + } + + public void drop(DnDEvent aEvent) { + setBorderPainted(false); + if (aEvent.getAttachedObject() instanceof PaletteItem) { + myGroup.handleDrop(myPaletteWindow.getProject(), (PaletteItem) aEvent.getAttachedObject(), -1); + } + } + + public void cleanUpOnLeave() { + setBorderPainted(false); + } + + public void updateDraggedImage(Image image, Point dropPoint, Point imageOffset) { + } + }, this); + + addFocusListener(new FocusAdapter() { + @Override public void focusGained(FocusEvent e) { + myPaletteWindow.setLastFocusedGroup(PaletteGroupHeader.this); + } + }); + + initActions(); + } + + public void showGroupPopupMenu(final Component comp, final int x, final int y) { + ActionGroup group = myGroup.getPopupActionGroup(); + if (group != null) { + ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group); + popupMenu.getComponent().show(comp, x, y); + } + } + + private void initActions() { + @NonNls InputMap inputMap = getInputMap(WHEN_FOCUSED); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "moveFocusDown"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "moveFocusUp"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "collapse"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "expand"); + + @NonNls ActionMap actionMap = getActionMap(); + actionMap.put("moveFocusDown", new MoveFocusAction(true)); + actionMap.put("moveFocusUp", new MoveFocusAction(false)); + actionMap.put("collapse", new ExpandAction(false)); + actionMap.put("expand", new ExpandAction(true)); + } + + @Override public Color getBackground() { + if (isFocusOwner()) { + return UIUtil.getListSelectionBackground(); + } + return super.getBackground(); + } + + @Override public Color getForeground() { + if (isFocusOwner()) { + return UIUtil.getListSelectionForeground(); + } + return super.getForeground(); + } + + public void setComponentList(final PaletteComponentList componentList) { + myComponentList = componentList; + } + + public PaletteComponentList getComponentList() { + return myComponentList; + } + + public PaletteGroup getGroup() { + return myGroup; + } + + @Nullable public Object getData(String dataId) { + Object data = myPaletteWindow.getData(dataId); + if (data != null) return data; + Project project = CommonDataKeys.PROJECT.getData(myPaletteWindow); + return myGroup.getData(project, dataId); + } + + private class MoveFocusAction extends AbstractAction { + private final boolean moveDown; + + public MoveFocusAction(boolean moveDown) { + this.moveDown = moveDown; + } + + public void actionPerformed(ActionEvent e) { + KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + Container container = kfm.getCurrentFocusCycleRoot(); + FocusTraversalPolicy policy = container.getFocusTraversalPolicy(); + if (null == policy) policy = kfm.getDefaultFocusTraversalPolicy(); + Component next = + moveDown ? policy.getComponentAfter(container, PaletteGroupHeader.this) : policy.getComponentBefore(container, PaletteGroupHeader.this); + if (null != next && next instanceof PaletteComponentList) { + final PaletteComponentList list = (PaletteComponentList)next; + if (list.getModel().getSize() != 0) { + list.takeFocusFrom(PaletteGroupHeader.this, list == myComponentList ? 0 : -1); + return; + } + else { + next = moveDown ? policy.getComponentAfter(container, next) : policy.getComponentBefore(container, next); + } + } + if (null != next && next instanceof PaletteGroupHeader) { + next.requestFocus(); + } + } + } + + private class ExpandAction extends AbstractAction { + private final boolean expand; + + public ExpandAction(boolean expand) { + this.expand = expand; + } + + public void actionPerformed(ActionEvent e) { + if (expand == isSelected()) return; + setSelected(expand); + if (myComponentList != null) { + myComponentList.setVisible(isSelected()); + } + } + } +} diff --git a/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteToolWindowManager.java b/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteToolWindowManager.java new file mode 100644 index 000000000000..c6fe8756d1f5 --- /dev/null +++ b/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteToolWindowManager.java @@ -0,0 +1,118 @@ +/* + * Copyright 2000-2014 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.designer.DesignerEditorPanelFacade; +import com.intellij.designer.LightToolWindow; +import com.intellij.icons.AllIcons; +import com.intellij.ide.IdeBundle; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.ToolWindowAnchor; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.ui.content.Content; +import com.intellij.ui.content.ContentManager; +import com.intellij.uiDesigner.AbstractToolWindowManager; +import com.intellij.uiDesigner.designSurface.GuiEditor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Alexander Lobas + */ +public class PaletteToolWindowManager extends AbstractToolWindowManager { + private final PaletteWindow myToolWindowPanel; + + public PaletteToolWindowManager(Project project, FileEditorManager fileEditorManager) { + super(project, fileEditorManager); + myToolWindowPanel = ApplicationManager.getApplication().isHeadlessEnvironment() ? null : new PaletteWindow(project); + } + + public static PaletteWindow getInstance(GuiEditor designer) { + PaletteToolWindowManager manager = getInstance(designer.getProject()); + if (manager.isEditorMode()) { + return (PaletteWindow)manager.getContent(designer); + } + return manager.myToolWindowPanel; + } + + public static PaletteToolWindowManager getInstance(Project project) { + return project.getComponent(PaletteToolWindowManager.class); + } + + @Override + protected void initToolWindow() { + myToolWindow = ToolWindowManager.getInstance(myProject) + .registerToolWindow(IdeBundle.message("toolwindow.palette"), false, getAnchor(), myProject, true); + myToolWindow.setIcon(AllIcons.Toolwindows.ToolWindowPalette); + initGearActions(); + + ContentManager contentManager = myToolWindow.getContentManager(); + Content content = contentManager.getFactory().createContent(myToolWindowPanel, null, false); + content.setCloseable(false); + content.setPreferredFocusableComponent(myToolWindowPanel); + contentManager.addContent(content); + contentManager.setSelectedContent(content, true); + myToolWindow.setAvailable(false, null); + } + + @Override + protected void updateToolWindow(@Nullable DesignerEditorPanelFacade designer) { + myToolWindowPanel.refreshPaletteIfChanged((GuiEditor)designer); + + if (designer == null) { + myToolWindow.setAvailable(false, null); + } + else { + myToolWindow.setAvailable(true, null); + myToolWindow.show(null); + } + } + + @Override + protected ToolWindowAnchor getAnchor() { + return ToolWindowAnchor.RIGHT; + } + + @Override + protected LightToolWindow createContent(@NotNull DesignerEditorPanelFacade designer) { + PaletteWindow palettePanel = new PaletteWindow(myProject); + palettePanel.refreshPaletteIfChanged((GuiEditor)designer); + + return createContent(designer, + palettePanel, + IdeBundle.message("toolwindow.palette"), + AllIcons.Toolwindows.ToolWindowPalette, + palettePanel, + palettePanel, + 180, + null); + } + + @Override + public void disposeComponent() { + if (myToolWindowPanel != null) { + myToolWindowPanel.dispose(); + } + } + + @NotNull + @Override + public String getComponentName() { + return "PaletteManager"; + } +}
\ No newline at end of file diff --git a/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteWindow.java b/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteWindow.java new file mode 100644 index 000000000000..0c7b57ce999c --- /dev/null +++ b/plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteWindow.java @@ -0,0 +1,351 @@ +/* + * 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.ide.palette.impl; + +import com.intellij.designer.LightToolWindowContent; +import com.intellij.ide.palette.PaletteGroup; +import com.intellij.ide.palette.PaletteItem; +import com.intellij.ide.palette.PaletteItemProvider; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.PopupHandler; +import com.intellij.ui.ScrollPaneFactory; +import com.intellij.ui.components.JBTabbedPane; +import com.intellij.uiDesigner.designSurface.GuiEditor; +import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.HashSet; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.*; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceAdapter; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.event.KeyEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.*; + +/** + * @author yole + */ +public class PaletteWindow extends JPanel implements LightToolWindowContent, DataProvider { + private final Project myProject; + private final ArrayList<PaletteGroupHeader> myGroupHeaders = new ArrayList<PaletteGroupHeader>(); + private final PaletteItemProvider[] myProviders; + private final MyPropertyChangeListener myPropertyChangeListener = new MyPropertyChangeListener(); + private final Set<PaletteGroup> myGroups = new HashSet<PaletteGroup>(); + private final JTabbedPane myTabbedPane = new JBTabbedPane(); + private final JScrollPane myScrollPane = ScrollPaneFactory.createScrollPane(); + private final MyListSelectionListener myListSelectionListener = new MyListSelectionListener(); + private PaletteGroupHeader myLastFocusedGroup; + + @NonNls private static final String ourHelpID = "guiDesigner.uiTour.palette"; + + private final DragSourceListener myDragSourceListener = new DragSourceAdapter() { + @Override + public void dragDropEnd(DragSourceDropEvent event) { + Component component = event.getDragSourceContext().getComponent(); + if (!event.getDropSuccess() && + component instanceof PaletteComponentList && + getRootPane() == ((JComponent)component).getRootPane()) { + clearActiveItem(); + } + } + }; + + private GuiEditor myDesigner; + + public PaletteWindow(Project project) { + myProject = project; + myProviders = Extensions.getExtensions(PaletteItemProvider.EP_NAME, project); + + setLayout(new GridLayout(1, 1)); + myScrollPane.addMouseListener(new MyScrollPanePopupHandler()); + myScrollPane.setBorder(null); + KeyStroke escStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + new ClearActiveItemAction().registerCustomShortcutSet(new CustomShortcutSet(escStroke), myScrollPane); + + if (!ApplicationManager.getApplication().isHeadlessEnvironment()) { + DragSource.getDefaultDragSource().addDragSourceListener(myDragSourceListener); + } + } + + public void dispose() { + removePaletteProviderListener(); + + if (!ApplicationManager.getApplication().isHeadlessEnvironment()) { + DragSource.getDefaultDragSource().removeDragSourceListener(myDragSourceListener); + } + } + + private void addPaletteProviderListener() { + for (PaletteItemProvider provider : myProviders) { + provider.addListener(myPropertyChangeListener); + } + } + + private void removePaletteProviderListener() { + for (PaletteItemProvider provider : myProviders) { + provider.removeListener(myPropertyChangeListener); + } + } + + public void refreshPaletteIfChanged(@Nullable GuiEditor designer) { + removePaletteProviderListener(); + myDesigner = designer; + if (designer != null) { + addPaletteProviderListener(); + } + + VirtualFile file = designer == null ? null : designer.getFile(); + Set<PaletteGroup> currentGroups = new HashSet<PaletteGroup>(collectCurrentGroups(file)); + if (!currentGroups.equals(myGroups)) { + refreshPalette(file); + } + } + + private void refreshPalette(@Nullable VirtualFile selectedFile) { + for (PaletteGroupHeader groupHeader : myGroupHeaders) { + groupHeader.getComponentList().removeListSelectionListener(myListSelectionListener); + } + String[] oldTabNames = collectTabNames(myGroups); + myTabbedPane.removeAll(); + myGroupHeaders.clear(); + myGroups.clear(); + + final ArrayList<PaletteGroup> currentGroups = collectCurrentGroups(selectedFile); + String[] tabNames = collectTabNames(currentGroups); + if (tabNames.length == 1) { + if (oldTabNames.length != 1) { + remove(myTabbedPane); + add(myScrollPane); + } + + PaletteContentWindow contentWindow = new PaletteContentWindow(); + myScrollPane.getViewport().setView(contentWindow); + + for (PaletteGroup group : currentGroups) { + addGroupToControl(group, contentWindow); + } + + final JComponent view = (JComponent)myScrollPane.getViewport().getView(); + if (view != null) { + view.revalidate(); + for (Component component : view.getComponents()) { + ((JComponent)component).revalidate(); + } + } + } + else { + if (oldTabNames.length <= 1) { + remove(myScrollPane); + add(myTabbedPane); + } + for (String tabName : tabNames) { + PaletteContentWindow contentWindow = new PaletteContentWindow(); + JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(contentWindow); + scrollPane.addMouseListener(new MyScrollPanePopupHandler()); + myTabbedPane.add(tabName, scrollPane); + for (PaletteGroup group : currentGroups) { + if (group.getTabName().equals(tabName)) { + addGroupToControl(group, contentWindow); + } + } + } + myTabbedPane.revalidate(); + } + } + + private void addGroupToControl(PaletteGroup group, JComponent control) { + PaletteGroupHeader groupHeader = new PaletteGroupHeader(this, group); + myGroupHeaders.add(groupHeader); + myGroups.add(group); + control.add(groupHeader); + PaletteComponentList componentList = new PaletteComponentList(myProject, this, group); + control.add(componentList); + groupHeader.setComponentList(componentList); + componentList.addListSelectionListener(myListSelectionListener); + } + + private static String[] collectTabNames(final Collection<PaletteGroup> groups) { + Set<String> result = new TreeSet<String>(); + for (PaletteGroup group : groups) { + result.add(group.getTabName()); + } + return ArrayUtil.toStringArray(result); + } + + private ArrayList<PaletteGroup> collectCurrentGroups(@Nullable VirtualFile selectedFile) { + ArrayList<PaletteGroup> result = new ArrayList<PaletteGroup>(); + if (selectedFile != null) { + for (PaletteItemProvider provider : myProviders) { + PaletteGroup[] groups = provider.getActiveGroups(selectedFile); + Collections.addAll(result, groups); + } + } + return result; + } + + public void clearActiveItem() { + if (getActiveItem() == null) return; + for (PaletteGroupHeader group : myGroupHeaders) { + group.getComponentList().clearSelection(); + } + ListSelectionEvent event = new ListSelectionEvent(this, -1, -1, false); + notifySelectionChanged(event); + } + + @Nullable + public PaletteItem getActiveItem() { + for (PaletteGroupHeader groupHeader : myGroupHeaders) { + if (groupHeader.isSelected() && groupHeader.getComponentList().getSelectedValue() != null) { + return (PaletteItem)groupHeader.getComponentList().getSelectedValue(); + } + } + return null; + } + + @Nullable + public <T extends PaletteItem> T getActiveItem(Class<T> cls) { + PaletteItem item = getActiveItem(); + if (item != null && item.getClass().isInstance(item)) { + //noinspection unchecked + return (T)item; + } + return null; + } + + @Nullable + public Object getData(String dataId) { + if (PlatformDataKeys.HELP_ID.is(dataId)) { + return ourHelpID; + } + if (CommonDataKeys.PROJECT.is(dataId)) { + return myProject; + } + PaletteItem item = getActiveItem(); + if (item != null) { + Object data = item.getData(myProject, dataId); + if (data != null) return data; + } + for (PaletteGroupHeader groupHeader : myGroupHeaders) { + if ((groupHeader.isSelected() && groupHeader.getComponentList().getSelectedValue() != null) || groupHeader == myLastFocusedGroup) { + return groupHeader.getGroup().getData(myProject, dataId); + } + } + final int tabCount = collectTabNames(myGroups).length; + if (tabCount > 0) { + JScrollPane activeScrollPane; + if (tabCount == 1) { + activeScrollPane = myScrollPane; + } + else { + activeScrollPane = (JScrollPane)myTabbedPane.getSelectedComponent(); + } + PaletteContentWindow activeContentWindow = (PaletteContentWindow)activeScrollPane.getViewport().getView(); + PaletteGroupHeader groupHeader = activeContentWindow.getLastGroupHeader(); + if (groupHeader != null) { + return groupHeader.getGroup().getData(myProject, dataId); + } + } + return null; + } + + public Project getProject() { + return myProject; + } + + void setLastFocusedGroup(final PaletteGroupHeader focusedGroup) { + myLastFocusedGroup = focusedGroup; + for (PaletteGroupHeader group : myGroupHeaders) { + group.getComponentList().clearSelection(); + } + } + + void notifyKeyEvent(final KeyEvent e) { + if (myDesigner != null) { + if (e.getID() == KeyEvent.KEY_PRESSED) { + myDesigner.paletteKeyPressed(e); + } + else if (e.getID() == KeyEvent.KEY_RELEASED) { + myDesigner.paletteKeyReleased(e); + } + } + } + + void notifyDropActionChanged(int gestureModifiers) { + if (myDesigner != null) { + myDesigner.paletteDropActionChanged(gestureModifiers); + } + } + + void notifySelectionChanged(final ListSelectionEvent event) { + if (myDesigner != null) { + myDesigner.paletteValueChanged(event); + } + } + + private class MyListSelectionListener implements ListSelectionListener { + public void valueChanged(ListSelectionEvent e) { + PaletteComponentList sourceList = (PaletteComponentList)e.getSource(); + for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) { + if (sourceList.isSelectedIndex(i)) { + // selection is being added + for (PaletteGroupHeader group : myGroupHeaders) { + if (group.getComponentList() != sourceList) { + group.getComponentList().clearSelection(); + } + } + break; + } + } + notifySelectionChanged(e); + } + } + + private class MyPropertyChangeListener implements PropertyChangeListener { + public void propertyChange(PropertyChangeEvent evt) { + refreshPalette(myDesigner.getFile()); + } + } + + private static class MyScrollPanePopupHandler extends PopupHandler { + public void invokePopup(Component comp, int x, int y) { + JScrollPane scrollPane = (JScrollPane)comp; + PaletteContentWindow contentWindow = (PaletteContentWindow)scrollPane.getViewport().getView(); + if (contentWindow != null) { + PaletteGroupHeader groupHeader = contentWindow.getLastGroupHeader(); + if (groupHeader != null) { + groupHeader.showGroupPopupMenu(comp, x, y); + } + } + } + } + + private class ClearActiveItemAction extends AnAction { + public void actionPerformed(AnActionEvent e) { + clearActiveItem(); + } + } +} |