ColumnControlButton.java
上传用户:zhengdagz
上传日期:2014-03-06
资源大小:1956k
文件大小:15k
源码类别:

xml/soap/webservice

开发平台:

Java

  1. /*
  2.  * $Id: ColumnControlButton.java,v 1.15 2005/10/13 08:59:56 kleopatra Exp $
  3.  *
  4.  * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
  5.  * Santa Clara, California 95054, U.S.A. All rights reserved.
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  * 
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  * 
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  20.  */
  21. package org.jdesktop.swingx.table;
  22. import java.awt.ComponentOrientation;
  23. import java.awt.Dimension;
  24. import java.awt.Insets;
  25. import java.awt.event.ActionEvent;
  26. import java.awt.event.ItemEvent;
  27. import java.beans.PropertyChangeEvent;
  28. import java.beans.PropertyChangeListener;
  29. import java.util.ArrayList;
  30. import java.util.Arrays;
  31. import java.util.Iterator;
  32. import java.util.List;
  33. import javax.swing.AbstractAction;
  34. import javax.swing.Action;
  35. import javax.swing.Icon;
  36. import javax.swing.JButton;
  37. import javax.swing.JComboBox;
  38. import javax.swing.JPopupMenu;
  39. import javax.swing.event.ChangeEvent;
  40. import javax.swing.event.ListSelectionEvent;
  41. import javax.swing.event.TableColumnModelEvent;
  42. import javax.swing.event.TableColumnModelListener;
  43. import javax.swing.table.TableColumn;
  44. import javax.swing.table.TableColumnModel;
  45. import org.jdesktop.swingx.JXTable;
  46. import org.jdesktop.swingx.action.AbstractActionExt;
  47. import org.jdesktop.swingx.action.ActionContainerFactory;
  48. /**
  49.  * This class is installed in the upper right corner of the table and is a
  50.  * control which allows for toggling the visibilty of individual columns.
  51.  * 
  52.  * TODO: the table reference is a potential leak
  53.  * 
  54.  * TODO: no need to extend JButton - use non-visual controller returning
  55.  * a JComponent instead.
  56.  * 
  57.  */
  58. public final class ColumnControlButton extends JButton {
  59.     /** exposed for testing. */
  60.     protected JPopupMenu popupMenu = null;
  61.     /** the table which is controlled by this. */
  62.     private JXTable table;
  63.     /** a marker to auto-recognize actions which should be added to the popup */
  64.     public static final String COLUMN_CONTROL_MARKER = "column.";
  65.     /** the list of actions for column menuitems.*/
  66.     private List<ColumnVisibilityAction> columnVisibilityActions;
  67.     public ColumnControlButton(JXTable table, Icon icon) {
  68.         super();
  69.         init();
  70.         setAction(createControlAction(icon));
  71.         installTable(table);
  72.     }
  73.     public void updateUI() {
  74.         super.updateUI();
  75.         setMargin(new Insets(1, 2, 2, 1)); // Make this LAF-independent
  76.         if (popupMenu != null) {
  77.             // JW: Hmm, not really working....
  78.             popupMenu.updateUI();
  79.         }
  80.     }
  81.    
  82. //-------------------------- Action in synch with column properties
  83.     /**
  84.      * A specialized action which takes care of keeping in synch with
  85.      * TableColumn state.
  86.      * 
  87.      * NOTE: client must call releaseColumn if this action is no longer needed!
  88.      * 
  89.      */
  90.     public class ColumnVisibilityAction extends AbstractActionExt {
  91.         private TableColumn column;
  92.         private PropertyChangeListener columnListener;
  93.         public ColumnVisibilityAction(TableColumn column) {
  94.             super((String) null);
  95.             setStateAction();
  96.             installColumn(column);
  97.         }
  98.         /**
  99.          * 
  100.          * release listening to column. Client must call this method if the
  101.          * action is no longer needed. After calling it the action must not be
  102.          * used any longer.
  103.          */
  104.         public void releaseColumn() {
  105.             column.removePropertyChangeListener(columnListener);
  106.             column = null;
  107.         }
  108.         public boolean isEnabled() {
  109.             return super.isEnabled() && canControl();
  110.         }
  111.         private boolean canControl() {
  112.             return (column instanceof TableColumnExt);
  113.         }
  114.         public void itemStateChanged(ItemEvent e) {
  115.             if (canControl()) {
  116.                 if ((e.getStateChange() == ItemEvent.DESELECTED)
  117.                         && (table.getColumnCount() <= 1)) {
  118.                     reselect();
  119.                 } else {
  120.                     ((TableColumnExt) column)
  121.                             .setVisible(e.getStateChange() == ItemEvent.SELECTED);
  122.                 }
  123.             }
  124.         }
  125.         public void actionPerformed(ActionEvent e) {
  126.             // TODO Auto-generated method stub
  127.         }
  128.         private void updateSelected() {
  129.             boolean visible = true;
  130.             if (canControl()) {
  131.                 visible = ((TableColumnExt) column).isVisible();
  132.             }
  133.             setSelected(visible);
  134.         }
  135.         private void reselect() {
  136.             firePropertyChange("selected", null, Boolean.TRUE);
  137.         }
  138.         // -------------- init
  139.         private void installColumn(TableColumn column) {
  140.             this.column = column;
  141.             column.addPropertyChangeListener(getColumnListener());
  142.             setName(String.valueOf(column.getHeaderValue()));
  143.             setActionCommand(column.getIdentifier());
  144.             updateSelected();
  145.         }
  146.         private PropertyChangeListener getColumnListener() {
  147.             if (columnListener == null) {
  148.                 columnListener = createPropertyChangeListener();
  149.             }
  150.             return columnListener;
  151.         }
  152.         private PropertyChangeListener createPropertyChangeListener() {
  153.             PropertyChangeListener l = new PropertyChangeListener() {
  154.                 public void propertyChange(PropertyChangeEvent evt) {
  155.                     if ("visible".equals(evt.getPropertyName())) {
  156.                         updateSelected();
  157.                     } else if ("headerValue".equals(evt.getPropertyName())) {
  158.                         setName(String.valueOf(evt.getNewValue()));
  159.                     }
  160.                 }
  161.             };
  162.             return l;
  163.         }
  164.     }
  165.     /** 
  166.      * open the popup. 
  167.      * 
  168.      * hmmm... really public?
  169.      * 
  170.      *
  171.      */ 
  172.     public void togglePopup() {
  173.         if (popupMenu.isVisible()) {
  174.             popupMenu.setVisible(false);
  175.         } else if (popupMenu.getComponentCount() > 0) {
  176.             Dimension buttonSize = getSize();
  177.             popupMenu.show(this, buttonSize.width
  178.                     - popupMenu.getPreferredSize().width, buttonSize.height);
  179.         }
  180.     }
  181.     @Override
  182.     public void applyComponentOrientation(ComponentOrientation o) {
  183.         super.applyComponentOrientation(o);
  184.         popupMenu.applyComponentOrientation(o);
  185.     }
  186. //-------------------------- updates from table propertyChangelistnere
  187.     
  188.     /**
  189.      * adjust internal state to after table's column model property has changed.
  190.      * Handles cleanup of listeners to the old/new columnModel (listens to the
  191.      * new only if we can control column visibility) and content of popupMenu.
  192.      * 
  193.      * @param oldModel
  194.      */
  195.     private void updateFromColumnModelChange(TableColumnModel oldModel) {
  196.         if (oldModel != null) {
  197.             oldModel.removeColumnModelListener(columnModelListener);
  198.         }
  199.         populatePopupMenu();
  200.         if (canControl()) {
  201.             table.getColumnModel().addColumnModelListener(columnModelListener);
  202.         }
  203.     }
  204.     
  205.     protected void updateFromTableEnabledChanged() {
  206.         getAction().setEnabled(table.isEnabled());
  207.         
  208.     }
  209.     /**
  210.      * Method to check if we can control column visibility POST: if true we can
  211.      * be sure to have an extended TableColumnModel
  212.      * 
  213.      * @return
  214.      */
  215.     private boolean canControl() {
  216.         return table.getColumnModel() instanceof TableColumnModelExt;
  217.     }
  218.  
  219. //  ------------------------ updating the popupMenu
  220.     /**
  221.      * Populates the popup menu with actions related to controlling columns.
  222.      * Adds an action for each columns in the table (including both visible and
  223.      * hidden). Adds all column-related actions from the table's actionMap.
  224.      */
  225.     protected void populatePopupMenu() {
  226.         clearPopupMenu();
  227.         if (canControl()) {
  228.             addColumnMenuItems();
  229.         }
  230.         addColumnActions();
  231.     }
  232.     /**
  233.      * 
  234.      * removes all components from the popup, making sure to release all
  235.      * columnVisibility actions.
  236.      * 
  237.      */
  238.     private void clearPopupMenu() {
  239.         clearColumnVisibilityActions();
  240.         popupMenu.removeAll();
  241.     }
  242.     /**
  243.      * release actions and clear list of actions.
  244.      * 
  245.      */
  246.     private void clearColumnVisibilityActions() {
  247.         if (columnVisibilityActions == null)
  248.             return;
  249.         for (Iterator<ColumnVisibilityAction> iter = columnVisibilityActions
  250.                 .iterator(); iter.hasNext();) {
  251.             iter.next().releaseColumn();
  252.         }
  253.         columnVisibilityActions.clear();
  254.     }
  255.    
  256.     /**
  257.      * adds a action to toggle column visibility for every column currently in
  258.      * the table. This includes both visible and invisible columns.
  259.      * 
  260.      * pre: canControl()
  261.      * 
  262.      */
  263.     private void addColumnMenuItems() {
  264.         // For each column in the view, add a JCheckBoxMenuItem to popup
  265.         List columns = table.getColumns(true);
  266.         ActionContainerFactory factory = new ActionContainerFactory(null);
  267.         for (Iterator iter = columns.iterator(); iter.hasNext();) {
  268.             TableColumn column = (TableColumn) iter.next();
  269.             ColumnVisibilityAction action = new ColumnVisibilityAction(column);
  270.             getColumnVisibilityActions().add(action);
  271.             popupMenu.add(factory.createMenuItem(action));
  272.         }
  273.     }
  274.     private List<ColumnVisibilityAction> getColumnVisibilityActions() {
  275.         if (columnVisibilityActions == null) {
  276.             columnVisibilityActions = new ArrayList<ColumnVisibilityAction>();
  277.         }
  278.         return columnVisibilityActions;
  279.     }
  280.     /**
  281.      * add additional actions from the xtable's actionMap.
  282.      * 
  283.      */
  284.     private void addColumnActions() {
  285.         Object[] actionKeys = getColumnControlActionKeys();
  286.         Arrays.sort(actionKeys);
  287.         List actions = new ArrayList();
  288.         for (int i = 0; i < actionKeys.length; i++) {
  289.             if (isColumnControlActionKey(actionKeys[i])) {
  290.                 actions.add(table.getActionMap().get(actionKeys[i]));
  291.             }
  292.         }
  293.         if (actions.size() == 0)
  294.             return;
  295.         if (canControl()) {
  296.             popupMenu.addSeparator();
  297.         }
  298.         ActionContainerFactory factory = new ActionContainerFactory(null);
  299.         for (Iterator iter = actions.iterator(); iter.hasNext();) {
  300.             Action action = (Action) iter.next();
  301.             popupMenu.add(factory.createMenuItem(action));
  302.         }
  303.     }
  304.     /**
  305.      * @return
  306.      */
  307.     private Object[] getColumnControlActionKeys() {
  308.         Object[] allKeys = table.getActionMap().allKeys();
  309.         List columnKeys = new ArrayList();
  310.         for (int i = 0; i < allKeys.length; i++) {
  311.             if (isColumnControlActionKey(allKeys[i])) {
  312.                 columnKeys.add(allKeys[i]);
  313.             }
  314.         }
  315.         return columnKeys.toArray();
  316.     }
  317.     private boolean isColumnControlActionKey(Object actionKey) {
  318.         return (actionKey instanceof String) && ((String) actionKey).startsWith(COLUMN_CONTROL_MARKER);
  319.     }
  320.     //--------------------------- init
  321.     private void installTable(JXTable table) {
  322.         this.table = table;
  323.         table.addPropertyChangeListener(columnModelChangeListener);
  324.         updateFromColumnModelChange(null);
  325.         updateFromTableEnabledChanged();
  326.     }
  327.     /**
  328.      * Initialize the column control button's gui
  329.      */
  330.     private void init() {
  331.         setFocusPainted(false);
  332.         setFocusable(false);
  333.         // create the popup menu
  334.         popupMenu = new JPopupMenu();
  335.         // this is a trick to get hold of the client prop which
  336.         // prevents closing of the popup
  337.         JComboBox box = new JComboBox();
  338.         Object preventHide = box.getClientProperty("doNotCancelPopup");
  339.         putClientProperty("doNotCancelPopup", preventHide);
  340.     }
  341.     /** 
  342.      * the action created for this.
  343.      * 
  344.      * @param icon
  345.      * @return
  346.      */
  347.     private Action createControlAction(Icon icon) {
  348.         Action control = new AbstractAction() {
  349.             public void actionPerformed(ActionEvent e) {
  350.                 togglePopup();
  351.             }
  352.         };
  353.         control.putValue(Action.SMALL_ICON, icon);
  354.         return control;
  355.     }
  356.     
  357.     // -------------------------------- listeners
  358.     private PropertyChangeListener columnModelChangeListener = new PropertyChangeListener() {
  359.         public void propertyChange(PropertyChangeEvent evt) {
  360.             if ("columnModel".equals(evt.getPropertyName())) {
  361.                 updateFromColumnModelChange((TableColumnModel) evt.getOldValue());
  362.             } else if ("enabled".equals(evt.getPropertyName())) {
  363.                 updateFromTableEnabledChanged();
  364.             }
  365.         }
  366.     };
  367.     private TableColumnModelListener columnModelListener = new TableColumnModelListener() {
  368.         /** Tells listeners that a column was added to the model. */
  369.         public void columnAdded(TableColumnModelEvent e) {
  370.             // quickfix for #192
  371.             if (!isVisibilityChange(e, true)) {
  372.                 populatePopupMenu();
  373.             }
  374.         }
  375.         /** Tells listeners that a column was removed from the model. */
  376.         public void columnRemoved(TableColumnModelEvent e) {
  377.             if (!isVisibilityChange(e, false)) {
  378.                 populatePopupMenu();
  379.             }
  380.         }
  381.         /**
  382.          * check if the add/remove event is triggered by a move to/from the
  383.          * invisible columns.
  384.          * 
  385.          * PRE: the event must be received in columnAdded/Removed.
  386.          * 
  387.          * @param e
  388.          *            the received event
  389.          * @param added
  390.          *            if true the event is assumed to be received via
  391.          *            columnAdded, otherwise via columnRemoved.
  392.          * @return
  393.          */
  394.         private boolean isVisibilityChange(TableColumnModelEvent e,
  395.                 boolean added) {
  396.             // can't tell
  397.             if (!(e.getSource() instanceof DefaultTableColumnModelExt))
  398.                 return false;
  399.             DefaultTableColumnModelExt model = (DefaultTableColumnModelExt) e
  400.                     .getSource();
  401.             if (added) {
  402.                 return model.isAddedFromInvisibleEvent(e.getToIndex());
  403.             } else {
  404.                 return model.isRemovedToInvisibleEvent(e.getFromIndex());
  405.             }
  406.         }
  407.         /** Tells listeners that a column was repositioned. */
  408.         public void columnMoved(TableColumnModelEvent e) {
  409.         }
  410.         /** Tells listeners that a column was moved due to a margin change. */
  411.         public void columnMarginChanged(ChangeEvent e) {
  412.         }
  413.         /**
  414.          * Tells listeners that the selection model of the TableColumnModel
  415.          * changed.
  416.          */
  417.         public void columnSelectionChanged(ListSelectionEvent e) {
  418.         }
  419.     };
  420. } // end class ColumnControlButton