001    package jigcell.compare.ui;
002    
003    import java.awt.Color;
004    import java.awt.Component;
005    import java.awt.event.ActionListener;
006    import java.awt.event.KeyEvent;
007    import java.beans.PropertyChangeEvent;
008    import java.beans.PropertyChangeListener;
009    import javax.swing.AbstractCellEditor;
010    import javax.swing.DefaultCellEditor;
011    import javax.swing.JButton;
012    import javax.swing.JCheckBox;
013    import javax.swing.JComboBox;
014    import javax.swing.JTable;
015    import javax.swing.JTextField;
016    import javax.swing.KeyStroke;
017    import javax.swing.UIManager;
018    import javax.swing.table.AbstractTableModel;
019    import javax.swing.table.DefaultTableCellRenderer;
020    import javax.swing.table.TableCellEditor;
021    import javax.swing.table.TableCellRenderer;
022    import javax.swing.table.TableColumn;
023    import javax.swing.table.TableColumnModel;
024    import javax.swing.table.TableModel;
025    import jigcell.compare.ITab;
026    import jigcell.compare.impl.Compare;
027    import jigcell.compare.impl.Config;
028    
029    /**
030     * Extends JTable by enhancing the default table editor, renderer, and layout.  Provides additional support for building custom table models.
031     *
032     * <p>
033     * This code is licensed under the DARPA BioCOMP Open Source License.  See LICENSE for more details.
034     * </p>
035     *
036     * @author Nicholas Allen
037     */
038    
039    public class BasicTable extends JTable {
040    
041       /**
042        * Client property for delayed initialization
043        */
044    
045       protected final static String CLIENT_COMPONENTSHOWN = "jigcell_component_shown";
046    
047       /**
048        * Table model
049        */
050    
051       protected BasicTableModel model;
052    
053       /**
054        * Base class of the data model used by views.
055        */
056    
057       public abstract static class BasicTableModel extends AbstractTableModel {
058    
059          /**
060           * Weights for resizing columns
061           */
062    
063          protected double columnWeights [];
064    
065          /**
066           * Column markers of the table
067           */
068    
069          protected Marker markers [];
070    
071          /**
072           * Provides quick access to information about columns in this model.
073           */
074    
075          protected static class Marker {
076    
077             /**
078              * Unique identifier for this column.
079              */
080    
081             private final String identifier;
082    
083             /**
084              * Name of this column for the table header.
085              */
086    
087             private String name;
088    
089             /**
090              * Creates a new table marker.
091              *
092              * @param identifier Identifier for this table column
093              * @param name Name for the table column in this model
094              */
095    
096             public Marker (String identifier, String name) {
097                assert identifier != null;
098                this.identifier = identifier;
099                this.name = name;
100             }
101    
102             public boolean equals (Object o) {
103                return o instanceof Marker && ((Marker) o).identifier.equals (identifier);
104             }
105    
106             /**
107              * The name of this column for the table header.
108              */
109    
110             public String getName () {
111                return name;
112             }
113    
114             public int hashCode () {
115                return identifier.hashCode ();
116             }
117    
118             /**
119              * Sets the name of this column for the table header.
120              *
121              * @param name Name
122              */
123    
124             public void setName (String name) {
125                this.name = name;
126             }
127    
128             public String toString () {
129                return getName ();
130             }
131          }
132    
133          /**
134           * Creates a new table model.
135           */
136    
137          public BasicTableModel () {}
138    
139          /**
140           * Fills along a column with a particular value.
141           *
142           * @param column Column
143           * @param startRow First row
144           * @param endRow Last row
145           * @param value Value
146           */
147    
148          public void fillRange (int column, int startRow, int endRow, Object value) {
149             while (startRow <= endRow)
150                setValueAt (value, startRow++, column);
151          }
152    
153          /**
154           * Fills along a column.
155           *
156           * @param row Row
157           * @param column Column
158           * @param startRow First row
159           * @param endRow Last row
160           */
161    
162          public void fillRangeAt (int row, int column, int startRow, int endRow) {
163             fillRange (column, startRow, endRow, getValueAt (row, column));
164          }
165    
166          /**
167           * @see jigcell.compare.ui.ICellEditorTab#fillSelected(int,int,int[])
168           */
169    
170          public void fillSelected (int row, int column, int rows []) {
171             for (int i = 0, l = rows.length; i < l; i++) {
172                int selection = rows [i];
173                fillRangeAt (row, column, selection, selection);
174             }
175          }
176    
177          /**
178           * The index of a column.  If no column has the specified marker, the result is -1.
179           *
180           * @param marker Column marker
181           */
182    
183          public int findColumn (Marker marker) {
184             assert marker != null;
185             for (int i = markers.length - 1; i >= 0; i--)
186                if (markers [i].equals (marker))
187                   return i;
188             return -1;
189          }
190    
191          /**
192           * The type of objects in a column.
193           *
194           * @param column Index of column
195           */
196    
197          public Class getColumnClass (int column) {
198             return String.class;
199          }
200    
201          /**
202           * The number of columns in the table.
203           */
204    
205          public int getColumnCount () {
206             return markers.length;
207          }
208    
209          /**
210           * The name of a column.
211           *
212           * @param column Index of column
213           */
214    
215          public String getColumnName (int column) {
216             return column >= 0 && column < markers.length ? markers [column].getName () : "";
217          }
218    
219          public double [] getColumnWeights () {
220             return columnWeights;
221          }
222    
223          /**
224           * {@inheritDoc}
225           */
226    
227          public Object getValueAt (int row, int column) {
228             return null;
229          }
230    
231          public void setColumnWeights (double columnWeights []) {
232             assert columnWeights == null || columnWeights.length == getColumnCount ();
233             this.columnWeights = columnWeights;
234          }
235    
236          /**
237           * Sets the table markers used by this mode.
238           *
239           * @param markers Table markers
240           */
241    
242          protected void setColumnMarkers (Marker markers []) {
243             assert markers != null;
244             this.markers = markers;
245          }
246       }
247    
248       /**
249        * Default editor for a BasicTable.
250        */
251    
252       public static class BasicEditor extends DefaultCellEditor implements PropertyChangeListener {
253    
254          /**
255           * Configuration property key for the modified cell foreground color
256           */
257    
258          public final static String CONFIG_FOREGROUNDMODIFIED = "BasicTable.foregroundModifiedCell";
259    
260          /**
261           * Default foreground color for modified cells
262           */
263    
264          protected final static Color DEFAULT_FOREGROUNDMODIFIED = new Color (0, 0, 255);
265    
266          /**
267           * Host view of this table
268           */
269    
270          private IBasicTableHost host;
271    
272          /**
273           * Creates a new editor.
274           */
275    
276          public BasicEditor () {
277             this (null, new JTextField ());
278          }
279    
280          /**
281           * Creates a new editor.
282           *
283           * @param host Host view of this table
284           */
285    
286          public BasicEditor (IBasicTableHost host) {
287             this (host, new JTextField ());
288          }
289    
290          /**
291           * Creates a new editor.
292           *
293           * @param host Host view of this table
294           * @param checkBox Editor control
295           */
296    
297          public BasicEditor (IBasicTableHost host, JCheckBox checkBox) {
298             super (checkBox);
299             registerHost (host);
300          }
301    
302          /**
303           * Creates a new editor.
304           *
305           * @param host Host view of this table
306           * @param comboBox Editor control
307           */
308    
309          public BasicEditor (IBasicTableHost host, JComboBox comboBox) {
310             super (comboBox);
311             registerHost (host);
312          }
313    
314          /**
315           * Creates a new editor.
316           *
317           * @param host Host view of this table
318           * @param textField Editor control
319           */
320    
321          public BasicEditor (IBasicTableHost host, JTextField textField) {
322             super (textField);
323             registerHost (host);
324          }
325    
326          /**
327           * A color for use with this table.
328           *
329           * @param key Color key
330           */
331    
332          public Color getColor (String key) {
333             return UIManager.getColor (mungeKey (key));
334          }
335    
336          public Component getTableCellEditorComponent (JTable table, Object value, boolean selected, int row, int column) {
337             Component cell = super.getTableCellEditorComponent (table, value, selected, row, column);
338             cell.setForeground (getColor ("BasicEditor.foregroundModified"));
339             return cell;
340          }
341    
342          public void propertyChange (PropertyChangeEvent e) {
343             if (Compare.PROPERTY_CONFIG_EDIT.equals (e.getPropertyName ()))
344                readConfiguration (ITab.STATE_RUNNING);
345          }
346    
347          /**
348           * Sets a color for use with this editor.
349           *
350           * @param key Color key
351           * @param color Color
352           */
353    
354          public void putColor (String key, Color color) {
355             UIManager.put (mungeKey (key), color);
356          }
357    
358          /**
359           * @see jigcell.compare.ITab#readConfiguration(java.lang.String)
360           */
361    
362          public void readConfiguration (String state) {
363             putColor ("BasicEditor.foregroundModified", getColorFromConfig (CONFIG_FOREGROUNDMODIFIED, DEFAULT_FOREGROUNDMODIFIED));
364          }
365    
366          /**
367           * Color specified by the configuration of the host view.
368           *
369           * @param key Configuration key
370           * @param defaultColor Color to use if configuration does not specify a value
371           */
372    
373          protected Color getColorFromConfig (String key, Color defaultColor) {
374             return host == null ? defaultColor : Config.convertToColor (host.getCompare ().getConfig ().findValue (host.getConfigMarkers (), key),
375                defaultColor);
376          }
377    
378          /**
379           * Creates the actual key value based on a generic key value.
380           *
381           * @param key Key
382           */
383    
384          private String mungeKey (String key) {
385             return (host == null ? getClass ().toString () : host.getHostIdentifier ()) + "%" + key;
386          }
387    
388          /**
389           * Hooks this editor up to the host so that configuration data can be obtained.  This method should only be called once and before
390           * any configuration data is needed.
391           *
392           * @param host Host view of this table
393           */
394    
395          private void registerHost (IBasicTableHost host) {
396             this.host = host;
397             putColor ("BasicEditor.foregroundModified", DEFAULT_FOREGROUNDMODIFIED);
398             if (host == null)
399                return;
400             host.getCompare ().addPropertyChangeListener (this);
401             readConfiguration (ITab.STATE_INITIALIZE);
402          }
403       }
404    
405       /**
406        * Default renderer for a BasicTable.
407        */
408    
409       public static class BasicRenderer extends DefaultTableCellRenderer implements PropertyChangeListener {
410    
411          /**
412           * Configuration property key for the editable cell background color
413           */
414    
415          public final static String CONFIG_BACKGROUNDEDITABLE = "BasicTable.backgroundEditableCell";
416    
417          /**
418           * Configuration property key for the uneditable cell background color
419           */
420    
421          public final static String CONFIG_BACKGROUNDUNEDITABLE = "BasicTable.backgroundUneditableCell";
422    
423          /**
424           * Default background color for editable cells
425           */
426    
427          protected final static Color DEFAULT_BACKGROUNDEDITABLE = new Color (255, 255, 255);
428    
429          /**
430           * Default backgorund color for uneditable cells
431           */
432    
433          protected final static Color DEFAULT_BACKGROUNDUNEDITABLE = new Color (240, 240, 240);
434    
435          /**
436           * Host view of this table
437           */
438    
439          private IBasicTableHost host;
440    
441          /**
442           * Creates a new renderer.
443           */
444    
445          public BasicRenderer () {
446             this (null);
447          }
448    
449          /**
450           * Creates a new renderer.
451           *
452           * @param host Host view of this table
453           */
454    
455          public BasicRenderer (IBasicTableHost host) {
456             super ();
457             registerHost (host);
458          }
459    
460          /**
461           * A color for use with this renderer.
462           *
463           * @param key Color key
464           */
465    
466          public Color getColor (String key) {
467             return UIManager.getColor (mungeKey (key));
468          }
469    
470          public Component getTableCellRendererComponent (JTable table, Object value, boolean selected, boolean focus, int row, int column) {
471             Component cell = super.getTableCellRendererComponent (table, value, selected, focus, row, column);
472             if (!selected)
473                cell.setBackground (table.isCellEditable (row, column) ? getColor ("BasicRenderer.backgroundEditable") :
474                   getColor ("BasicRenderer.backgroundUneditable"));
475             return cell;
476          }
477    
478          public void propertyChange (PropertyChangeEvent e) {
479             if (Compare.PROPERTY_CONFIG_EDIT.equals (e.getPropertyName ()))
480                readConfiguration (ITab.STATE_RUNNING);
481          }
482    
483          /**
484           * Sets a color for use with this renderer.
485           *
486           * @param key Color key
487           * @param color Color
488           */
489    
490          public void putColor (String key, Color color) {
491             UIManager.put (mungeKey (key), color);
492          }
493    
494          /**
495           * @see jigcell.compare.ITab#readConfiguration(java.lang.String)
496           */
497    
498          public void readConfiguration (String state) {
499             putColor ("BasicRenderer.backgroundEditable", getColorFromConfig (CONFIG_BACKGROUNDEDITABLE, DEFAULT_BACKGROUNDEDITABLE));
500             putColor ("BasicRenderer.backgroundUneditable", getColorFromConfig (CONFIG_BACKGROUNDUNEDITABLE, DEFAULT_BACKGROUNDUNEDITABLE));
501             if (host != null)
502                host.getTable ().repaint ();
503          }
504    
505          /**
506           * Color specified by the configuration of the host view.
507           *
508           * @param key Configuration key
509           * @param defaultColor Color to use if configuration does not specify a value
510           */
511    
512          protected Color getColorFromConfig (String key, Color defaultColor) {
513             return host == null ? defaultColor : Config.convertToColor (host.getCompare ().getConfig ().findValue (host.getConfigMarkers (), key),
514                defaultColor);
515          }
516    
517          /**
518           * Creates the actual key value based on a generic key value.
519           *
520           * @param key Key
521           */
522    
523          private String mungeKey (String key) {
524             return (host == null ? getClass ().toString () : host.getHostIdentifier ()) + "%" + key;
525          }
526    
527          /**
528           * Hooks this renderer up to the host so that configuration data can be obtained.  This method should only be called once and before
529           * any configuration data is needed.
530           *
531           * @param host Host view of this table
532           */
533    
534          private void registerHost (IBasicTableHost host) {
535             this.host = host;
536             putColor ("BasicRenderer.backgroundEditable", DEFAULT_BACKGROUNDEDITABLE);
537             putColor ("BasicRenderer.backgroundUneditable", DEFAULT_BACKGROUNDUNEDITABLE);
538             if (host == null)
539                return;
540             host.getCompare ().addPropertyChangeListener (this);
541             readConfiguration (ITab.STATE_INITIALIZE);
542          }
543       }
544    
545       /**
546        * Editor that displays a button in a table cell
547        */
548    
549       public abstract static class ButtonEditor extends AbstractCellEditor implements ActionListener, TableCellEditor {
550    
551          /**
552           * Column being edited
553           */
554    
555          protected int column;
556    
557          /**
558           * Row being edited
559           */
560    
561          protected int row;
562    
563          /**
564           * Button
565           */
566    
567          protected JButton button;
568    
569          /**
570           * Editor value
571           */
572    
573          protected Object value;
574    
575          /**
576           * Creates a new button editor.
577           */
578    
579          public ButtonEditor () {
580             button = new JButton ();
581             button.addActionListener (this);
582          }
583    
584          public Object getCellEditorValue () {
585             return value;
586          }
587    
588          public Component getTableCellEditorComponent (JTable table, Object value, boolean isSelected, int row, int column) {
589             this.row = row;
590             this.column = column;
591             this.value = value;
592             button.setText (value == null ? "" : value.toString ());
593             return button;
594          }
595    
596          /**
597           * Column being edited.
598           */
599    
600          protected int getColumn () {
601             return column;
602          }
603    
604          /**
605           * Row being edited.
606           */
607    
608          protected int getRow () {
609             return row;
610          }
611       }
612    
613       /**
614        * Renderer for displaying a button in a table cell
615        */
616    
617       public static class ButtonRenderer extends JButton implements TableCellRenderer {
618    
619          /**
620           * Creates a new button renderer.
621           */
622    
623          public ButtonRenderer () {}
624    
625          public Component getTableCellRendererComponent (JTable table, Object value, boolean selected, boolean focus, int row, int column) {
626             setText (value == null ? "" : value.toString ());
627             return this;
628          }
629       }
630    
631       /**
632        * Computes the widths for table columns, trying to make every column the same size.
633        *
634        * @param maxWidth Maximum width of a column
635        * @param parentWidth Width of the table
636        * @param currentWidths Current widths of the columns
637        */
638    
639       protected static int [] resizeColumnsEqually (int maxWidth, int parentWidth, int currentWidths []) {
640          int columnCount = currentWidths.length;
641          int width = parentWidth / columnCount;
642          if (width >= maxWidth) {
643             int pos = 0;
644             for (int _width = width + 1, totalWidth = width * columnCount; totalWidth < parentWidth; pos++, totalWidth++)
645                currentWidths [pos] = _width;
646             for (; pos < columnCount; pos++)
647                currentWidths [pos] = width;
648             return currentWidths;
649          }
650          int totalWidth = 0;
651          for (int i = 0; i < columnCount; i++)
652             totalWidth += currentWidths [i];
653          for (int minPos; totalWidth < parentWidth; currentWidths [minPos]++, totalWidth++) {
654             minPos = 0;
655             int minWidth = currentWidths [minPos];
656             for (int i = 1; i < columnCount; i++) {
657                width = currentWidths [i];
658                if (width > minWidth)
659                   continue;
660                minWidth = width;
661                minPos = i;
662             }
663          }
664          return currentWidths;
665       }
666    
667       /**
668        * Computes the widths for table columns, trying to make the ratios of column width to column weight the same.  Column sizes will not be
669        * decreased below the requested minimums even if this will make the columns too large to fit in the view.  Columns with a weight of 0 are
670        * never given more space than they start with.
671        *
672        * @param maxWidth Maximum width of a column
673        * @param parentWidth Width of the table
674        * @param minWidths Minimum widths of the columns
675        * @param currentWidths Current widths of the columns
676        * @param columnWeights Column weights
677        */
678    
679       protected static int [] resizeColumnsUnequally (int maxWidth, int parentWidth, int minWidths [], int currentWidths [],
680          double columnWeights []) {
681          int columnCount = columnWeights.length;
682          int startPos;
683          for (startPos = 0; startPos < columnCount; startPos++)
684             if (columnWeights [startPos] != 0.0)
685                break;
686          if (startPos == columnCount)
687             return resizeColumnsEqually (maxWidth, parentWidth, currentWidths);
688          int totalMinWidth = 0;
689          maxWidth = 0;
690          for (int i = 0; i < columnCount; i++) {
691             int width = minWidths [i];
692             totalMinWidth += width;
693             if (width > maxWidth)
694                maxWidth = width;
695          }
696          if (totalMinWidth > parentWidth)
697             return minWidths;
698          int totalWidth = 0;
699          double columnDeltas [] = new double [columnCount];
700          for (int i = 0; i < columnCount; i++) {
701             int width = currentWidths [i];
702             totalWidth += width;
703             double weight = columnWeights [i];
704             columnDeltas [i] = weight == 0.0 ? Double.NaN : (width - maxWidth) / weight;
705          }
706          for (startPos = 0; Double.isNaN (columnDeltas [startPos]); )
707             startPos++;
708          for (; totalWidth < parentWidth; totalWidth++) {
709             int bestPos = startPos;
710             double bestDelta = columnDeltas [bestPos];
711             for (int i = startPos + 1; i < columnCount; i++) {
712                double delta = columnDeltas [i];
713                if (delta < bestDelta) {
714                   bestPos = i;
715                   bestDelta = delta;
716                }
717             }
718             columnDeltas [bestPos] = (++currentWidths [bestPos] - maxWidth) / columnWeights [bestPos];
719          }
720          if (totalWidth == parentWidth)
721             return currentWidths;
722          for (int i = 0; i < columnCount; i++)
723             if (currentWidths [i] == minWidths [i])
724                columnDeltas [i] = Double.NaN;
725          while (startPos < columnCount && Double.isNaN (columnDeltas [startPos]))
726             startPos++;
727          if (startPos == columnCount)
728             return currentWidths;
729          for (int i = startPos; i < columnCount; i++) {
730             double weight = columnWeights [i];
731             columnDeltas [i] = weight == 0.0 ? Double.NaN : (currentWidths [i] - maxWidth) / weight;
732          }
733          for (; totalWidth > parentWidth; totalWidth--) {
734             int bestPos = startPos;
735             double bestDelta = columnDeltas [bestPos];
736             for (int i = startPos + 1; i < columnCount; i++) {
737                double delta = columnDeltas [i];
738                if (delta > bestDelta) {
739                   bestPos = i;
740                   bestDelta = delta;
741                }
742             }
743             int width = --currentWidths [bestPos];
744             if (width != minWidths [bestPos]) {
745                columnDeltas [bestPos] = (width - maxWidth) / columnWeights [bestPos];
746                continue;
747             }
748             columnDeltas [bestPos] = Double.NaN;
749             if (bestPos != startPos)
750                continue;
751             startPos++;
752             while (startPos < columnCount && Double.isNaN (columnDeltas [startPos]))
753                startPos++;
754             if (startPos == columnCount)
755                return currentWidths;
756          }
757          return currentWidths;
758       }
759    
760       /**
761        * Creates a new table.
762        *
763        * @param model Model
764        */
765    
766       public BasicTable (BasicTableModel model) {
767          super (model);
768          setFocusCycleRoot (true);
769          setDefaultRenderer (Object.class, new BasicRenderer ());
770          setDefaultEditor (Object.class, new BasicEditor ());
771       }
772    
773       public void doLayout () {
774          if (getClientProperty (CLIENT_COMPONENTSHOWN) == null) {
775             putClientProperty (CLIENT_COMPONENTSHOWN, this);
776             TableCellRenderer renderer = getTableHeader ().getDefaultRenderer ();
777             TableColumnModel columnModel = getColumnModel ();
778             double columnWeights [] = model.getColumnWeights ();
779             if (columnWeights == null)
780                for (int i = 0, l = getColumnCount (); i < l; i++)
781                   columnModel.getColumn (i).setMinWidth (renderer.getTableCellRendererComponent (this, getColumnName (i), false, false, -1, i)
782                      .getPreferredSize ().width + 5);
783             else {
784                for (int i = 0, l = getColumnCount (); i < l; i++) {
785                   TableColumn column = columnModel.getColumn (i);
786                   int width = renderer.getTableCellRendererComponent (this, getColumnName (i), false, false, -1, i).getPreferredSize ().width + 5;
787                   column.setMinWidth (width);
788                   if (columnWeights [i] == 0.0)
789                      column.setMaxWidth (width);
790                }
791             }
792          }
793          if (getTableHeader ().getResizingColumn () == null)
794             resizeColumns ();
795          super.doLayout ();
796       }
797    
798       /**
799        * The index of a column in this table.  If no displayed column has the specified marker, the result is -1.
800        *
801        * @param marker Column marker
802        */
803    
804       public int findColumn (BasicTableModel.Marker marker) {
805          return convertColumnIndexToView (model.findColumn (marker));
806       }
807    
808       public Component prepareEditor (TableCellEditor editor, int row, int column) {
809          Component component = super.prepareEditor (editor, row, column);
810          if (component instanceof JButton)
811             return component;
812          component.requestFocus ();
813          if (component instanceof JTextField)
814             ((JTextField) component).selectAll ();
815          return component;
816       }
817    
818       /**
819        * Sets the widths for table columns.
820        */
821    
822       public void resizeColumns () {
823          int columnCount = getColumnCount ();
824          int maxWidth = 0;
825          int totalWidth = 0;
826          int currentWidths [] = new int [columnCount];
827          TableColumnModel columnModel = getColumnModel ();
828          for (int i = 0; i < columnCount; i++) {
829             int width = currentWidths [i] = columnModel.getColumn (i).getWidth ();
830             totalWidth += width;
831             if (width > maxWidth)
832                maxWidth = width;
833          }
834          double columnWeights [] = model.getColumnWeights ();
835          if (columnWeights == null)
836             currentWidths = resizeColumnsEqually (maxWidth, getParent ().getWidth (), currentWidths);
837          else {
838             int minWidths [] = new int [columnCount];
839             for (int i = 0; i < columnCount; i++)
840                minWidths [i] = columnModel.getColumn (i).getMinWidth ();
841             currentWidths = resizeColumnsUnequally (maxWidth, getParent ().getWidth (), minWidths, currentWidths, columnWeights);
842          }
843          for (int i = 0; i < columnCount; i++)
844             columnModel.getColumn (i).setPreferredWidth (currentWidths [i]);
845       }
846    
847       public void setModel (TableModel model) {
848          if (this.model != null)
849             throw new IllegalStateException ("BasicTable model can only be set by constructor.");
850          if (!(model instanceof BasicTableModel))
851             throw new IllegalArgumentException ("BasicTable can only be used with a BasicTableModel.");
852          super.setModel (model);
853          this.model = (BasicTableModel) model;
854       }
855    
856       protected boolean processKeyBinding (KeyStroke keyStroke, KeyEvent e, int condition, boolean pressed) {
857          if (e.isAltDown () && !e.isAltGraphDown () || e.isControlDown () || e.isMetaDown ())
858             return false;
859          return super.processKeyBinding (keyStroke, e, condition, pressed);
860       }
861    }