KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > palette > ui > CategoryList


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20
21 package org.netbeans.modules.palette.ui;
22
23 import java.awt.BorderLayout JavaDoc;
24 import java.awt.Color JavaDoc;
25 import java.awt.Component JavaDoc;
26 import java.awt.Container JavaDoc;
27 import java.awt.FocusTraversalPolicy JavaDoc;
28 import java.awt.Image JavaDoc;
29 import java.awt.Insets JavaDoc;
30 import java.awt.KeyboardFocusManager JavaDoc;
31 import java.awt.Point JavaDoc;
32 import java.awt.Rectangle JavaDoc;
33 import java.awt.dnd.Autoscroll JavaDoc;
34 import java.awt.event.ActionEvent JavaDoc;
35 import java.awt.event.KeyEvent JavaDoc;
36 import java.awt.event.MouseEvent JavaDoc;
37 import java.beans.BeanInfo JavaDoc;
38 import java.lang.ref.WeakReference JavaDoc;
39 import javax.swing.AbstractAction JavaDoc;
40 import javax.swing.Action JavaDoc;
41 import javax.swing.ActionMap JavaDoc;
42 import javax.swing.BorderFactory JavaDoc;
43 import javax.swing.ImageIcon JavaDoc;
44 import javax.swing.InputMap JavaDoc;
45 import javax.swing.JComponent JavaDoc;
46 import javax.swing.JList JavaDoc;
47 import javax.swing.JPopupMenu JavaDoc;
48 import javax.swing.JToggleButton JavaDoc;
49 import javax.swing.JToolBar JavaDoc;
50 import javax.swing.KeyStroke JavaDoc;
51 import javax.swing.ListCellRenderer JavaDoc;
52 import javax.swing.ListSelectionModel JavaDoc;
53 import javax.swing.SwingConstants JavaDoc;
54 import javax.swing.SwingUtilities JavaDoc;
55 import javax.swing.UIManager JavaDoc;
56 import javax.swing.border.Border JavaDoc;
57 import javax.swing.border.EmptyBorder JavaDoc;
58 import javax.swing.event.MouseInputListener JavaDoc;
59 import javax.swing.plaf.basic.BasicListUI JavaDoc;
60 import org.netbeans.modules.palette.Category;
61 import org.netbeans.modules.palette.Item;
62 import org.netbeans.modules.palette.Utils;
63 import org.openide.nodes.Node;
64 import org.openide.util.Utilities;
65
66 /**
67  * Specialized JList for palette items (content of a palette category) - having
68  * special UI and renderer providing fine-tuned alignment, rollover effect,
69  * showing names and different icon size. Used by CategoryDescriptor.
70  *
71  * @author Tomas Pavek, S. Aubrecht
72  */

73
74 public class CategoryList extends JList JavaDoc implements Autoscroll JavaDoc {
75
76     private int rolloverIndex = -1;
77     private boolean showNames;
78
79     static final int BASIC_ICONSIZE = BeanInfo.ICON_COLOR_16x16;
80     private int iconSize = BASIC_ICONSIZE;
81     
82     private Category category;
83     private PalettePanel palettePanel;
84
85     private static WeakReference JavaDoc<ListCellRenderer JavaDoc> rendererRef;
86     
87     private Item draggingItem;
88     
89     private AutoscrollSupport support;
90
91     /**
92      * Constructor.
93      */

94     CategoryList( Category category, PalettePanel palettePanel ) {
95         this.category = category;
96         this.palettePanel = palettePanel;
97         setBackground(UIManager.getColor ("Panel.background"));
98         setBorder (new EmptyBorder JavaDoc (0, 0, 0, 0));
99         setVisibleRowCount (0);
100         setSelectionMode (ListSelectionModel.SINGLE_SELECTION);
101         setCellRenderer (getItemRenderer ());
102         setLayoutOrientation( HORIZONTAL_WRAP );
103         getAccessibleContext().setAccessibleName( category.getDisplayName() );
104         getAccessibleContext().setAccessibleDescription( category.getShortDescription() );
105
106         initActions();
107     }
108     
109     private void initActions() {
110         InputMap JavaDoc inputMap = getInputMap(JComponent.WHEN_FOCUSED);
111         inputMap.put( KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0, false ), "defaultAction" );
112         inputMap.put( KeyStroke.getKeyStroke( KeyEvent.VK_F10, KeyEvent.SHIFT_DOWN_MASK, false ), "popup" );
113
114         ActionMap JavaDoc map = getActionMap();
115         map.put( "defaultAction", new DefaultAction( this ) );
116         map.put( "popup", new PopupAction() );
117         map.put( "selectPreviousRow", new MoveFocusAction( map.get( "selectPreviousRow" ), false ) );
118         map.put( "selectNextRow", new MoveFocusAction( map.get( "selectNextRow" ), true ) );
119         map.put( "selectPreviousColumn", new MoveFocusAction( new ChangeColumnAction( map.get( "selectPreviousColumn" ), false ), false ) );
120         map.put( "selectNextColumn", new MoveFocusAction( new ChangeColumnAction( map.get( "selectNextColumn" ), true ), true ) );
121         Node categoryNode = (Node)category.getLookup().lookup( Node.class );
122         if( null != categoryNode )
123             map.put( "paste", new Utils.PasteItemAction( categoryNode ) );
124         else
125             map.remove( "paste" );
126         map.put( "copy", new CutCopyAction( true ) );
127         map.put( "cut", new CutCopyAction( false ) );
128     }
129
130     Item getItemAt( int index ) {
131         if( index < 0 || index >= getModel().getSize() )
132             return null;
133         
134         return (Item)getModel().getElementAt( index );
135     }
136
137     Category getCategory() {
138         return category;
139     }
140
141     public void updateUI () {
142         if( null != rendererRef )
143             rendererRef.clear();
144         setUI (new CategoryListUI ());
145         invalidate ();
146     }
147
148     // Workaround for issue 39037. Due to the following method we can
149
// use getPreferredSize() in the implementation of the method
150
// getPreferredHeight(). Otherwise we would have to copy the content
151
// of getPreferredSize() into the layout manager of the enclosing JScrollPane.
152
// We cannot change the width directly through setBounds() method
153
// because it would force another repaint.
154
Integer JavaDoc tempWidth;
155
156     public int getWidth () {
157         return (tempWidth == null) ? super.getWidth () : tempWidth.intValue ();
158     }
159
160     // ---------
161

162     boolean getShowNames () {
163         return showNames;
164     }
165
166     void setShowNames (boolean show) {
167         if (show != showNames) {
168             showNames = show;
169             firePropertyChange ("cellRenderer", null, null); // NOI18N
170
}
171     }
172
173     int getIconSize () {
174         return iconSize;
175     }
176
177     void setIconSize (int size) {
178         if (size != iconSize) {
179             iconSize = size;
180             firePropertyChange ("cellRenderer", null, null); // NOI18N
181
}
182     }
183
184     // workaround for bug 4832765, which can cause the
185
// scroll pane to not let the user easily scroll up to the beginning
186
// of the list. An alternative would be to set the unitIncrement
187
// of the JScrollBar to a fixed value. You wouldn't get the nice
188
// aligned scrolling, but it should work.
189
public int getScrollableUnitIncrement (Rectangle JavaDoc visibleRect, int orientation, int direction) {
190         int row;
191         if (orientation == SwingConstants.VERTICAL &&
192                 direction < 0 && (row = getFirstVisibleIndex ()) != -1) {
193             Rectangle JavaDoc r = getCellBounds (row, row);
194             if ((r.y == visibleRect.y) && (row != 0)) {
195                 Point JavaDoc loc = r.getLocation ();
196                 loc.y--;
197                 int prevIndex = locationToIndex (loc);
198                 Rectangle JavaDoc prevR = getCellBounds (prevIndex, prevIndex);
199
200                 if (prevR == null || prevR.y >= r.y) {
201                     return 0;
202                 }
203                 return prevR.height;
204             }
205         }
206         return super.getScrollableUnitIncrement (visibleRect, orientation, direction);
207     }
208
209     /**
210      * Returns preferred height of the list for the specified <code>width</code>.
211      *
212      * @return preferred height of the list for the specified <code>width</code>.
213      */

214     public int getPreferredHeight (int width) {
215         return ((CategoryListUI) getUI ()).getPreferredHeight (width);
216     }
217     
218     public void resetRollover() {
219         rolloverIndex = -1;
220         repaint();
221     }
222     
223     int getColumnCount() {
224         if( getModel().getSize() > 0 ) {
225             Insets JavaDoc insets = getInsets ();
226             int listWidth = getWidth () - (insets.left + insets.right);
227             int cellWidth = getCellBounds( 0, 0 ).width;
228             if( listWidth >= cellWidth ) {
229                 return listWidth / cellWidth;
230             }
231         }
232         return 1;
233     }
234
235     // --------
236
// list item renderer
237

238     private static ListCellRenderer JavaDoc getItemRenderer () {
239         ListCellRenderer JavaDoc renderer = rendererRef == null ? null : rendererRef.get ();
240         if (renderer == null) {
241             renderer = new ItemRenderer ();
242             rendererRef = new WeakReference JavaDoc<ListCellRenderer JavaDoc>( renderer );
243         }
244         return renderer;
245     }
246
247     static class ItemRenderer implements ListCellRenderer JavaDoc {
248
249         /** toolbar containing the button, null for GTK L&F */
250         private JToolBar JavaDoc toolbar;
251         /** toggle button used as renderer component */
252         private JToggleButton JavaDoc button;
253
254         ItemRenderer () {
255             if (button == null) {
256                 button = new JToggleButton JavaDoc ();
257                 button.setMargin (new Insets JavaDoc (1, 1, 1, 0));
258                 
259                 if (!CategoryButton.isGTK) {
260                     toolbar = new JToolBar JavaDoc ();
261                     toolbar.setRollover (true);
262                     toolbar.setFloatable (false);
263                     toolbar.setLayout (new BorderLayout JavaDoc (0, 0));
264                     toolbar.setBorder (BorderFactory.createEmptyBorder());
265                     toolbar.add (button);
266                 }
267             }
268         }
269
270         public Component JavaDoc getListCellRendererComponent (JList JavaDoc list,
271                 Object JavaDoc value,
272                 int index,
273                 boolean isSelected,
274                 boolean cellHasFocus) {
275             CategoryList categoryList = (CategoryList) list;
276             boolean showNames = categoryList.getShowNames ();
277             int iconSize = categoryList.getIconSize ();
278             
279             JComponent JavaDoc rendererComponent = toolbar != null ? toolbar : button;
280
281             Item item = (Item) value;
282             Image JavaDoc icon = item.getIcon (iconSize);
283             if (icon != null) {
284                 button.setIcon (new ImageIcon JavaDoc (icon));
285             }
286
287             button.setText (showNames ? item.getDisplayName () : null);
288             rendererComponent.setToolTipText( item.getShortDescription() ); // NOI18N
289

290             button.setSelected (isSelected);
291             //if (defaultBorder == null) { // Windows or Metal
292
// let the toolbar UI render the button according to "rollover"
293
button.getModel ().setRollover (index == categoryList.rolloverIndex && !isSelected);
294             /*} else { // Mac OS X and others - set the border explicitly
295                 button.setBorder (defaultBorder);
296             }*/

297             button.setBorderPainted ((index == categoryList.rolloverIndex) || isSelected);
298
299             button.setHorizontalAlignment (showNames ? SwingConstants.LEFT : SwingConstants.CENTER);
300             button.setHorizontalTextPosition (SwingConstants.RIGHT);
301             button.setVerticalTextPosition (SwingConstants.CENTER);
302
303             return rendererComponent;
304         }
305     }
306     
307     /** notify the Component to autoscroll */
308     public void autoscroll( Point JavaDoc cursorLoc ) {
309         if( null != getParent() && null != getParent().getParent() ) {
310             Point JavaDoc p = SwingUtilities.convertPoint( this, cursorLoc, getParent().getParent() );
311             getSupport().autoscroll( p );
312         }
313     }
314
315     /** @return the Insets describing the autoscrolling
316      * region or border relative to the geometry of the
317      * implementing Component.
318      */

319     public Insets JavaDoc getAutoscrollInsets() {
320         return getSupport().getAutoscrollInsets();
321     }
322
323     /** Safe getter for autoscroll support. */
324     AutoscrollSupport getSupport() {
325         if( null == support ) {
326             support = new AutoscrollSupport( getParent().getParent() );
327         }
328
329         return support;
330     }
331
332     /**
333      * Take focus from CategoryButton, i.e. select the first or the last item in the list
334      */

335     void takeFocusFrom( Component JavaDoc c ) {
336         int indexToSelect = -1;
337         if( c.getParent() != getParent() ) {
338             //this is not 'our' CategoryButton so we'll assume it's the one below this category list
339
indexToSelect = getModel().getSize()-1;
340         } else if( getModel().getSize() > 0 ) {
341             indexToSelect = 0;
342         }
343         requestFocus();
344         setSelectedIndex( indexToSelect );
345         if( indexToSelect >= 0 ) {
346             ensureIndexIsVisible( indexToSelect );
347         }
348     }
349
350     
351     // ---------
352
// list UI
353

354     static class CategoryListUI extends BasicListUI JavaDoc {
355
356         protected void updateLayoutState () {
357             super.updateLayoutState ();
358
359             if (list.getLayoutOrientation () == JList.HORIZONTAL_WRAP) {
360                 Insets JavaDoc insets = list.getInsets ();
361                 int listWidth = list.getWidth () - (insets.left + insets.right);
362                 if (listWidth >= cellWidth) {
363                     int columnCount = listWidth / cellWidth;
364                     cellWidth = (columnCount == 0) ? 1 : listWidth / columnCount;
365                 }
366             }
367         }
368
369         public int getPreferredHeight (int width) {
370             ((CategoryList) list).tempWidth = Integer.valueOf (width);
371             int result;
372             try {
373                 result = (int) getPreferredSize (list).getHeight ();
374             } finally {
375                 ((CategoryList) list).tempWidth = null;
376             }
377             return result;
378         }
379
380         protected MouseInputListener JavaDoc createMouseInputListener () {
381             return new ListMouseInputHandler ();
382         }
383
384         private class ListMouseInputHandler extends MouseInputHandler {
385
386                 int selIndex = -1;
387                 
388             public void mouseClicked(MouseEvent JavaDoc e) {
389                 if( !list.isEnabled() )
390                     return;
391                 
392                 if( e.getClickCount() > 1 ) {
393                     selIndex = getValidIndex( e.getPoint() );
394                     if( selIndex >= 0 ) {
395                         list.setSelectedIndex( selIndex );
396                         Item item = (Item)list.getModel().getElementAt( selIndex );
397                         ActionEvent JavaDoc ae = new ActionEvent JavaDoc( e.getSource(), e.getID(), "doubleclick", e.getWhen(), e.getModifiers() );
398                         item.invokePreferredAction( ae );
399                         e.consume();
400                     }
401                 }
402             }
403
404             public void mousePressed( MouseEvent JavaDoc e ) {
405                 if( getValidIndex( e.getPoint() ) >= 0 ) {
406                     selIndex = list.getSelectedIndex ();
407                     super.mousePressed (e);
408                 }
409             }
410
411             public void mouseDragged( MouseEvent JavaDoc e ) {
412             }
413
414             public void mouseMoved( MouseEvent JavaDoc e ) {
415                 mouseEntered( e );
416             }
417
418             public void mouseEntered( MouseEvent JavaDoc e ) {
419                 if( list.isEnabled() )
420                     setRolloverIndex( getValidIndex( e.getPoint() ) );
421             }
422
423             public void mouseExited( MouseEvent JavaDoc e ) {
424                 if( list.isEnabled() )
425                     setRolloverIndex( -1 );
426             }
427
428             public void mouseReleased( MouseEvent JavaDoc e ) {
429                 if( getValidIndex( e.getPoint() ) >= 0) {
430                     super.mouseReleased (e);
431                     if( selIndex > -1 && list.getSelectedIndex () == selIndex )
432                         list.removeSelectionInterval( selIndex, selIndex );
433                 }
434             }
435
436             private void setRolloverIndex (int index) {
437                 int oldIndex = ((CategoryList) list).rolloverIndex;
438                 if (index != oldIndex) {
439                     ((CategoryList) list).rolloverIndex = index;
440                     if (oldIndex > -1) {
441                         Rectangle JavaDoc r = getCellBounds (list, oldIndex, oldIndex);
442                         if (r != null)
443                             list.repaint (r.x, r.y, r.width, r.height);
444                     }
445                     if (index > -1) {
446                         Rectangle JavaDoc r = getCellBounds (list, index, index);
447                         if (r != null)
448                             list.repaint (r.x, r.y, r.width, r.height);
449                     }
450                 }
451             }
452         }
453
454         private int getValidIndex (Point JavaDoc p) {
455             int index = locationToIndex (list, p);
456             return index >= 0 && getCellBounds (list, index, index).contains (p) ?
457                     index : -1;
458         }
459     }
460     
461     private static class DefaultAction extends AbstractAction JavaDoc {
462         private CategoryList list;
463         public DefaultAction( CategoryList list ) {
464             this.list = list;
465         }
466
467         public void actionPerformed( ActionEvent JavaDoc e ) {
468             Item item = list.getItemAt( list.getSelectedIndex() );
469             item.invokePreferredAction( e );
470         }
471
472         public boolean isEnabled() {
473             return list.isEnabled() && list.getSelectedIndex() >= 0;
474         }
475     }
476     
477     private class PopupAction extends AbstractAction JavaDoc {
478
479         public void actionPerformed(ActionEvent JavaDoc e) {
480             int posX = 0;
481             int posY = 0;
482             Item item = getItemAt( getSelectedIndex() );
483             if( null != item ) {
484                 Rectangle JavaDoc rect = getCellBounds( getSelectedIndex(), getSelectedIndex() );
485                 posX = rect.x;
486                 posY = rect.y + rect.height;
487             }
488             Action JavaDoc[] actions = null == item ? category.getActions() : item.getActions();
489             JPopupMenu JavaDoc popup = Utilities.actionsToPopup( actions, CategoryList.this );
490             Utils.addCustomizationMenuItems( popup, palettePanel.getController(), palettePanel.getSettings() );
491             popup.show( getParent(), posX, posY );
492         }
493
494         public boolean isEnabled() {
495             return CategoryList.this.isEnabled();
496         }
497     }
498     
499     private class MoveFocusAction extends AbstractAction JavaDoc {
500         private Action JavaDoc defaultAction;
501         private boolean focusNext;
502                 
503         public MoveFocusAction( Action JavaDoc defaultAction, boolean focusNext ) {
504             this.defaultAction = defaultAction;
505             this.focusNext = focusNext;
506         }
507
508         public void actionPerformed(ActionEvent JavaDoc e) {
509             int selIndexBefore = getSelectedIndex();
510             defaultAction.actionPerformed( e );
511             int selIndexCurrent = getSelectedIndex();
512             if( selIndexBefore != selIndexCurrent )
513                 return;
514             
515             if( focusNext && 0 == selIndexCurrent )
516                 return;
517             
518             KeyboardFocusManager JavaDoc kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
519             Container JavaDoc container = kfm.getCurrentFocusCycleRoot();
520             FocusTraversalPolicy JavaDoc policy = container.getFocusTraversalPolicy();
521             if( null == policy )
522                 policy = kfm.getDefaultFocusTraversalPolicy();
523             Component JavaDoc next = focusNext ? policy.getComponentAfter( container, CategoryList.this )
524                                       : policy.getComponentBefore( container, CategoryList.this );
525             if( null != next && next instanceof CategoryButton ) {
526                 clearSelection();
527                 next.requestFocus();
528             }
529         }
530     }
531     
532     private class ChangeColumnAction extends AbstractAction JavaDoc {
533         private Action JavaDoc defaultAction;
534         private boolean selectNext;
535                 
536         public ChangeColumnAction( Action JavaDoc defaultAction, boolean selectNext ) {
537             this.defaultAction = defaultAction;
538             this.selectNext = selectNext;
539         }
540
541         public void actionPerformed(ActionEvent JavaDoc e) {
542             int selIndexBefore = getSelectedIndex();
543             defaultAction.actionPerformed( e );
544             int selIndexCurrent = getSelectedIndex();
545             if( (selectNext && selIndexBefore < selIndexCurrent)
546                 ||
547                 (!selectNext && selIndexBefore > selIndexCurrent) )
548                 return;
549             
550             if( selectNext ) {
551                 if( selIndexCurrent == selIndexBefore+1 )
552                     selIndexCurrent++;
553                 if( selIndexCurrent < getModel().getSize()-1 ) {
554                     setSelectedIndex( selIndexCurrent+1 );
555                     scrollRectToVisible( getCellBounds( selIndexCurrent+1, selIndexCurrent+1 ) );
556                 }
557             } else {
558                 if( selIndexCurrent > 0 ) {
559                     setSelectedIndex( selIndexCurrent-1 );
560                     scrollRectToVisible( getCellBounds( selIndexCurrent-1, selIndexCurrent-1 ) );
561                 }
562             }
563         }
564     }
565     
566     private class CutCopyAction extends AbstractAction JavaDoc {
567         private boolean doCopy;
568         public CutCopyAction( boolean doCopy ) {
569             this.doCopy = doCopy;
570         }
571
572         public void actionPerformed( ActionEvent JavaDoc e ) {
573             Item item = getItemAt( getSelectedIndex() );
574             if( null == item )
575                 return;
576             Node itemNode = (Node)item.getLookup().lookup( Node.class );
577             if( null == itemNode )
578                 return;
579             Action JavaDoc performer;
580             if( doCopy )
581                 performer = new Utils.CopyItemAction( itemNode );
582             else
583                 performer = new Utils.CutItemAction( itemNode );
584             if( performer.isEnabled() )
585                 performer.actionPerformed( e );
586         }
587
588         public boolean isEnabled() {
589             return getSelectedIndex() >= 0;
590         }
591     }
592 }
593
Popular Tags