2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.ide
.palette
.impl
;
18 import com
.intellij
.ide
.dnd
.*;
19 import com
.intellij
.ide
.palette
.PaletteGroup
;
20 import com
.intellij
.ide
.palette
.PaletteItem
;
21 import com
.intellij
.openapi
.actionSystem
.ActionGroup
;
22 import com
.intellij
.openapi
.actionSystem
.ActionManager
;
23 import com
.intellij
.openapi
.actionSystem
.ActionPlaces
;
24 import com
.intellij
.openapi
.actionSystem
.ActionPopupMenu
;
25 import com
.intellij
.openapi
.project
.Project
;
26 import com
.intellij
.openapi
.util
.Pair
;
27 import com
.intellij
.ui
.ColoredListCellRenderer
;
28 import com
.intellij
.ui
.PopupHandler
;
29 import com
.intellij
.util
.ui
.UIUtil
;
30 import org
.jetbrains
.annotations
.NonNls
;
31 import org
.jetbrains
.annotations
.Nullable
;
34 import javax
.swing
.plaf
.basic
.BasicListUI
;
36 import java
.awt
.event
.*;
41 public class PaletteComponentList
extends JList
{
42 private final Project myProject
;
43 private final PaletteGroup myGroup
;
44 private int myHoverIndex
= -1;
45 private int myBeforeClickSelectedRow
= -1;
46 private int myDropTargetIndex
= -1;
47 private boolean myNeedClearSelection
= false;
49 public PaletteComponentList(Project project
, PaletteGroup group
) {
52 setModel(new AbstractListModel() {
53 public int getSize() {
54 return myGroup
.getItems().length
;
57 public Object
getElementAt(int index
) {
58 return myGroup
.getItems() [index
];
62 addMouseListener(new MouseAdapter() {
63 @Override public void mouseEntered(MouseEvent e
) {
64 setHoverIndex(locationToIndex(e
.getPoint()));
67 @Override public void mouseExited(MouseEvent e
) {
71 @Override public void mousePressed(MouseEvent e
) {
72 myNeedClearSelection
= (SwingUtilities
.isLeftMouseButton(e
) &&
73 myBeforeClickSelectedRow
>= 0 &&
74 locationToIndex(e
.getPoint()) == myBeforeClickSelectedRow
&&
75 !UIUtil
.isControlKeyDown(e
) && !e
.isShiftDown());
78 @Override public void mouseReleased(MouseEvent e
) {
79 if (SwingUtilities
.isLeftMouseButton(e
) &&
80 myBeforeClickSelectedRow
>= 0 &&
81 locationToIndex(e
.getPoint()) == myBeforeClickSelectedRow
&&
82 !UIUtil
.isControlKeyDown(e
) && !e
.isShiftDown() && myNeedClearSelection
) {
88 addMouseListener(new PopupHandler() {
89 public void invokePopup(final Component comp
, final int x
, final int y
) {
90 requestFocusInWindow();
91 SwingUtilities
.invokeLater(new Runnable() {
93 int index
= locationToIndex(new Point(x
, y
));
94 PaletteItem
[] items
= myGroup
.getItems();
95 if (index
>= 0 && index
< items
.length
) {
96 if (getSelectedIndex() != index
) {
97 addSelectionInterval(index
, index
);
99 PaletteItem item
= items
[index
];
100 ActionGroup group
= item
.getPopupActionGroup();
102 ActionPopupMenu popupMenu
= ActionManager
.getInstance().createActionPopupMenu(ActionPlaces
.UNKNOWN
, group
);
103 popupMenu
.getComponent().show(comp
, x
, y
);
111 addMouseMotionListener(new MouseMotionAdapter() {
112 public void mouseMoved(MouseEvent e
) {
113 setHoverIndex(locationToIndex(e
.getPoint()));
117 addKeyListener(new KeyListener() {
118 public void keyPressed(KeyEvent e
) {
119 PaletteManager
.getInstance(myProject
).notifyKeyEvent(e
);
122 public void keyReleased(KeyEvent e
) {
123 PaletteManager
.getInstance(myProject
).notifyKeyEvent(e
);
126 public void keyTyped(KeyEvent e
) {
127 PaletteManager
.getInstance(myProject
).notifyKeyEvent(e
);
131 setCellRenderer(new ComponentCellRenderer());
133 setVisibleRowCount(0);
134 setLayoutOrientation(HORIZONTAL_WRAP
);
135 setSelectionMode(ListSelectionModel
.SINGLE_SELECTION
);
137 final DnDManager dndManager
= DnDManager
.getInstance();
138 dndManager
.registerSource(new MyDnDSource(), this);
139 dndManager
.registerTarget(new MyDnDTarget(), this);
144 private void setHoverIndex(final int index
) {
145 if (index
!= myHoverIndex
) {
146 if (myHoverIndex
>= 0) repaint(getCellBounds(myHoverIndex
, myHoverIndex
));
147 myHoverIndex
= index
;
148 if (myHoverIndex
>= 0) repaint(getCellBounds(myHoverIndex
, myHoverIndex
));
152 private void setDropTargetIndex(final int index
) {
153 if (index
!= myDropTargetIndex
) {
154 myDropTargetIndex
= index
;
159 @Override public void updateUI() {
160 setUI(new ComponentListUI());
164 private void initActions() {
165 @NonNls ActionMap map
= getActionMap();
166 map
.put( "selectPreviousRow", new MoveFocusAction( map
.get( "selectPreviousRow" ), false ) );
167 map
.put( "selectNextRow", new MoveFocusAction( map
.get( "selectNextRow" ), true ) );
168 map
.put( "selectPreviousColumn", new MoveFocusAction( new ChangeColumnAction( map
.get( "selectPreviousColumn" ), false ), false ) );
169 map
.put( "selectNextColumn", new MoveFocusAction( new ChangeColumnAction( map
.get( "selectNextColumn" ), true ), true ) );
174 public int getWidth () {
175 return (myTempWidth
== null) ?
super.getWidth () : myTempWidth
.intValue ();
178 public int getPreferredHeight(final int width
) {
181 return getUI().getPreferredSize(this).height
;
188 public void takeFocusFrom(PaletteGroupHeader paletteGroup
, int indexToSelect
) {
189 if (indexToSelect
== -1) {
190 //this is not 'our' CategoryButton so we'll assume it's the one below this category list
191 indexToSelect
= getModel().getSize() - 1;
193 else if (getModel().getSize() == 0) {
197 setSelectedIndex(indexToSelect
);
198 if (indexToSelect
>= 0) {
199 ensureIndexIsVisible(indexToSelect
);
203 @Override protected void paintComponent(Graphics g
) {
204 super.paintComponent(g
);
205 if (myDropTargetIndex
>= 0) {
208 if (myDropTargetIndex
== myGroup
.getItems().length
) {
209 rc
= getCellBounds(myDropTargetIndex
-1, myDropTargetIndex
-1);
210 dropLineY
= (int)rc
.getMaxY()-1;
213 rc
= getCellBounds(myDropTargetIndex
, myDropTargetIndex
);
216 Graphics2D g2d
= (Graphics2D
) g
;
217 g2d
.setColor(Color
.BLUE
);
218 g2d
.setStroke(new BasicStroke(2.0f
));
219 g2d
.drawLine(rc
.x
, dropLineY
, rc
.x
+rc
.width
, dropLineY
);
220 g2d
.drawLine(rc
.x
, dropLineY
-2, rc
.x
, dropLineY
+2);
221 g2d
.drawLine(rc
.x
+rc
.width
, dropLineY
-2, rc
.x
+rc
.width
, dropLineY
+2);
225 class ComponentListUI
extends BasicListUI
{
226 private ComponentListListener myListener
;
228 @Override protected void updateLayoutState() {
229 super.updateLayoutState();
231 if (list
.getLayoutOrientation() == JList
.HORIZONTAL_WRAP
) {
232 Insets insets
= list
.getInsets();
233 int listWidth
= list
.getWidth() - (insets
.left
+ insets
.right
);
234 if (listWidth
>= cellWidth
) {
235 int columnCount
= listWidth
/ cellWidth
;
236 cellWidth
= (columnCount
== 0) ?
1 : listWidth
/ columnCount
;
241 @Override protected void installListeners() {
242 myListener
= new ComponentListListener();
243 addMouseListener(myListener
);
244 super.installListeners();
248 protected void uninstallListeners() {
249 if (myListener
!= null) {
250 removeMouseListener(myListener
);
252 super.uninstallListeners();
255 private class ComponentListListener
extends MouseAdapter
{
256 @Override public void mousePressed(MouseEvent e
) {
257 myBeforeClickSelectedRow
= list
.getSelectedIndex();
262 private static class ComponentCellRenderer
extends ColoredListCellRenderer
{
263 protected void customizeCellRenderer(JList list
, Object value
, int index
, boolean selected
, boolean hasFocus
) {
264 PaletteItem paletteItem
= (PaletteItem
) value
;
266 paletteItem
.customizeCellRenderer(this, selected
, hasFocus
);
270 private class MoveFocusAction
extends AbstractAction
{
271 private final Action defaultAction
;
272 private final boolean focusNext
;
274 public MoveFocusAction(Action defaultAction
, boolean focusNext
) {
275 this.defaultAction
= defaultAction
;
276 this.focusNext
= focusNext
;
279 public void actionPerformed(ActionEvent e
) {
280 int selIndexBefore
= getSelectedIndex();
281 defaultAction
.actionPerformed(e
);
282 int selIndexCurrent
= getSelectedIndex();
283 if (selIndexBefore
!= selIndexCurrent
) return;
285 if (focusNext
&& 0 == selIndexCurrent
) return;
287 KeyboardFocusManager kfm
= KeyboardFocusManager
.getCurrentKeyboardFocusManager();
288 Container container
= kfm
.getCurrentFocusCycleRoot();
289 FocusTraversalPolicy policy
= container
.getFocusTraversalPolicy();
290 if (null == policy
) policy
= kfm
.getDefaultFocusTraversalPolicy();
291 Component next
= focusNext
292 ? policy
.getComponentAfter(container
, PaletteComponentList
.this)
293 : policy
.getComponentBefore(container
, PaletteComponentList
.this);
294 if (null != next
&& next
instanceof PaletteGroupHeader
) {
297 ((PaletteGroupHeader
)next
).scrollRectToVisible(next
.getBounds());
302 private class ChangeColumnAction
extends AbstractAction
{
303 private final Action defaultAction
;
304 private final boolean selectNext
;
306 public ChangeColumnAction(Action defaultAction
, boolean selectNext
) {
307 this.defaultAction
= defaultAction
;
308 this.selectNext
= selectNext
;
311 public void actionPerformed(ActionEvent e
) {
312 int selIndexBefore
= getSelectedIndex();
313 defaultAction
.actionPerformed(e
);
314 int selIndexCurrent
= getSelectedIndex();
315 if ((selectNext
&& selIndexBefore
< selIndexCurrent
) || (!selectNext
&& selIndexBefore
> selIndexCurrent
)) return;
318 if (selIndexCurrent
== selIndexBefore
+ 1) selIndexCurrent
++;
319 if (selIndexCurrent
< getModel().getSize() - 1) {
320 setSelectedIndex(selIndexCurrent
+ 1);
321 scrollRectToVisible(getCellBounds(selIndexCurrent
+ 1, selIndexCurrent
+ 1));
325 if (selIndexCurrent
> 0) {
326 setSelectedIndex(selIndexCurrent
- 1);
327 scrollRectToVisible(getCellBounds(selIndexCurrent
- 1, selIndexCurrent
- 1));
333 private class MyDnDTarget
implements DnDTarget
{
335 public boolean update(DnDEvent aEvent
) {
337 if (aEvent
.getAttachedObject() instanceof PaletteItem
) {
338 setDropTargetIndex(locationToTargetIndex(aEvent
.getPoint()));
339 aEvent
.setDropPossible(true, null);
342 setDropTargetIndex(-1);
343 aEvent
.setDropPossible(false, null);
348 public void drop(DnDEvent aEvent
) {
349 setDropTargetIndex(-1);
350 if (aEvent
.getAttachedObject() instanceof PaletteItem
) {
351 int index
= locationToTargetIndex(aEvent
.getPoint());
353 myGroup
.handleDrop(myProject
, (PaletteItem
) aEvent
.getAttachedObject(), index
);
358 public void cleanUpOnLeave() {
359 setDropTargetIndex(-1);
362 private int locationToTargetIndex(Point location
) {
363 int row
= locationToIndex(location
);
367 Rectangle rc
= getCellBounds(row
, row
);
368 return location
.y
< rc
.getCenterY() ? row
: row
+ 1;
371 public void updateDraggedImage(Image image
, Point dropPoint
, Point imageOffset
) {
375 private class MyDnDSource
implements DnDSource
{
376 public boolean canStartDragging(DnDAction action
, Point dragOrigin
) {
377 int index
= locationToIndex(dragOrigin
);
378 return index
>= 0 && myGroup
.getItems() [index
].startDragging() != null;
381 public DnDDragStartBean
startDragging(DnDAction action
, Point dragOrigin
) {
382 int index
= locationToIndex(dragOrigin
);
383 if (index
< 0) return null;
384 return myGroup
.getItems() [index
].startDragging();
388 public Pair
<Image
, Point
> createDraggedImage(DnDAction action
, Point dragOrigin
) {
392 public void dragDropEnd() {
395 public void dropActionChanged(final int gestureModifiers
) {
396 PaletteManager
.getInstance(myProject
).notifyDropActionChanged(gestureModifiers
);