001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the files COPYING and Copyright.html. *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * Or, see http://hdfgroup.org/products/hdf-java/doc/Copyright.html.         *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.view;
016
017import java.awt.Color;
018import java.awt.Component;
019import java.awt.event.ActionEvent;
020import java.awt.event.ActionListener;
021import java.awt.event.KeyEvent;
022import java.awt.event.KeyListener;
023import java.awt.event.MouseEvent;
024import java.io.BufferedInputStream;
025import java.io.BufferedWriter;
026import java.io.File;
027import java.io.FileWriter;
028import java.io.InputStream;
029import java.io.PrintWriter;
030import java.io.RandomAccessFile;
031import java.util.Enumeration;
032import java.util.HashMap;
033import java.util.Iterator;
034import java.util.List;
035import java.util.Map;
036
037import javax.print.Doc;
038import javax.print.DocFlavor;
039import javax.print.DocPrintJob;
040import javax.print.PrintService;
041import javax.print.PrintServiceLookup;
042import javax.print.SimpleDoc;
043import javax.print.StreamPrintServiceFactory;
044import javax.swing.CellEditor;
045import javax.swing.DefaultCellEditor;
046import javax.swing.JFileChooser;
047import javax.swing.JFrame;
048import javax.swing.JInternalFrame;
049import javax.swing.JLabel;
050import javax.swing.JMenu;
051import javax.swing.JMenuBar;
052import javax.swing.JMenuItem;
053import javax.swing.JOptionPane;
054import javax.swing.JPanel;
055import javax.swing.JScrollPane;
056import javax.swing.JTable;
057import javax.swing.JTextArea;
058import javax.swing.JTextField;
059import javax.swing.JViewport;
060import javax.swing.SwingConstants;
061import javax.swing.UIManager;
062import javax.swing.WindowConstants;
063import javax.swing.event.ChangeEvent;
064import javax.swing.event.ListSelectionEvent;
065import javax.swing.table.AbstractTableModel;
066import javax.swing.table.DefaultTableCellRenderer;
067import javax.swing.table.JTableHeader;
068import javax.swing.table.TableCellRenderer;
069import javax.swing.table.TableColumn;
070import javax.swing.table.TableColumnModel;
071
072import hdf.object.Dataset;
073import hdf.object.FileFormat;
074import hdf.object.HObject;
075import hdf.object.ScalarDS;
076
077/**
078 * TextView displays an HDF string dataset in text.
079 * 
080 * @author Peter X. Cao
081 * @version 2.4 9/6/2007
082 */
083public class DefaultTextView extends JInternalFrame implements TextView,
084        ActionListener, KeyListener {
085    private static final long serialVersionUID = 3892752752951438428L;
086
087    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultTextView.class);
088
089    /**
090     * The main HDFView.
091     */
092    private final ViewManager viewer;
093
094    /**
095     * The Scalar Dataset.
096     */
097    private ScalarDS dataset;
098
099    /**
100     * The string text.
101     */
102    private String[] text;
103
104    /** The table to display the text content */
105    private JTable table;
106
107    // Text areas to hold the text.
108    // private JTextArea[] textAreas;
109
110    private boolean isReadOnly = false;
111
112    private boolean isTextChanged = false;
113
114    private TextAreaEditor textEditor = null;
115
116    private RowHeader rowHeaders = null;
117    
118    private int indexBase = 0;
119
120    /**
121     * Constructs an TextView.
122     * <p>
123     * 
124     * @param theView
125     *            the main HDFView.
126     */
127    public DefaultTextView(ViewManager theView) {
128        this(theView, null);
129    }
130
131    /**
132     * Constructs an TextView.
133     *
134     * @param theView
135     *            the main HDFView.
136     * @param map
137     *            the properties on how to show the data. The map is used to
138     *            allow applications to pass properties on how to display the
139     *            data, such as, transposing data, showing data as character,
140     *            applying bitmask, and etc. Predefined keys are listed at
141     *            ViewProperties.DATA_VIEW_KEY.
142     */
143    public DefaultTextView(ViewManager theView, HashMap map) {
144        viewer = theView;
145        text = null;
146        table = null;
147        dataset = null;
148        textEditor = new TextAreaEditor(this);
149        
150        if (ViewProperties.isIndexBase1())
151            indexBase = 1;
152
153        HObject hobject = null;
154        if (map != null)
155            hobject = (HObject) map.get(ViewProperties.DATA_VIEW_KEY.OBJECT);
156        else
157            hobject = theView.getTreeView().getCurrentObject();
158
159        if (!(hobject instanceof ScalarDS)) {
160            return;
161        }
162
163        dataset = (ScalarDS) hobject;
164
165        if (!dataset.isText()) {
166            viewer.showStatus("Cannot display non-text dataset in text view.");
167            dataset = null;
168            return;
169        }
170
171        isReadOnly = dataset.getFileFormat().isReadOnly();
172
173        try {
174            text = (String[]) dataset.getData();
175        } 
176        catch (Exception ex) { 
177            JOptionPane.showMessageDialog(
178                    this,
179                    ex,
180                    "TextView:"+getTitle(),
181                    JOptionPane.ERROR_MESSAGE);
182            text = null; 
183        }
184
185        if (text == null) {
186            viewer.showStatus("Loading text dataset failed - "
187                    + dataset.getName());
188            dataset = null;
189            return;
190        }
191        
192        String fname = new java.io.File(dataset.getFile()).getName();
193        this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
194        this.setTitle("TextView  -  " + dataset.getName() + "  -  "
195                + dataset.getPath() + "  -  " + fname);
196        this.setFrameIcon(ViewProperties.getTextIcon());
197        this.setName("textdata");
198        
199        int rank = dataset.getRank();
200        long start[] = dataset.getStartDims();
201        long count[] = dataset.getSelectedDims();
202
203        String colName = "Data selection:   ["+start[0];
204        for (int i=1; i<rank; i++) {
205            colName += ", "+start[i];
206        }
207        colName += "] ~ ["+(start[0]+count[0]-1);
208        for (int i=1; i<rank; i++) {
209            colName += ", "+(start[i]+count[i]-1);
210        }
211        colName += "]";
212
213        table = createTable(colName);
214        
215        JTableHeader colHeader = table.getTableHeader();
216        colHeader.setReorderingAllowed(false);
217        //colHeader.setBackground(Color.black);
218        
219        rowHeaders = new RowHeader(table, dataset);
220
221        // add the table to a scroller
222        JScrollPane scrollingTable = new JScrollPane(table);
223        scrollingTable.getVerticalScrollBar().setUnitIncrement(100);
224        scrollingTable.getHorizontalScrollBar().setUnitIncrement(100);
225
226        JViewport viewp = new JViewport();
227        viewp.add(rowHeaders);
228        viewp.setPreferredSize(rowHeaders.getPreferredSize());
229        scrollingTable.setRowHeader(viewp);
230
231        TableColumnModel cmodel = table.getColumnModel();
232        TextAreaRenderer textAreaRenderer = new TextAreaRenderer();
233
234        cmodel.getColumn(0).setCellRenderer(textAreaRenderer);
235        cmodel.getColumn(0).setCellEditor(textEditor);
236
237        ((JPanel) getContentPane()).add(scrollingTable);
238
239        setJMenuBar(createMenuBar());
240    }
241
242    public void actionPerformed(ActionEvent e) {
243        Object source = e.getSource();
244        String cmd = e.getActionCommand();
245
246        if (cmd.equals("Close")) {
247            dispose(); // terminate the application
248        }
249        else if (cmd.equals("Save to text file")) {
250            try {
251                saveAsText();
252            }
253            catch (Exception ex) {
254                JOptionPane.showMessageDialog((JFrame) viewer, ex, getTitle(),
255                        JOptionPane.ERROR_MESSAGE);
256            }
257        }
258        else if (cmd.equals("Save changes")) {
259            updateValueInFile();
260        }
261        else if (cmd.equals("Print")) {
262            print();
263        }
264    }
265
266    /**
267     * Creates a Table to hold a compound dataset.
268     *
269     * @param colName the name of the column
270     */
271    private JTable createTable(final String colName) {
272        JTable theTable = null;
273        
274        AbstractTableModel tm =  new AbstractTableModel()
275        {
276            public int getColumnCount() {
277                return 1;
278            }
279
280            public int getRowCount() {
281                return text.length;
282            }
283
284            public String getColumnName(int col) {
285                return colName;
286            }
287
288            public Object getValueAt(int row, int column)
289            {
290                return text[row];
291            }
292        };
293        
294        theTable = new JTable(tm) {
295            private static final long serialVersionUID = -6571266777012522255L;
296
297            @Override
298            public boolean isCellEditable(int row, int column) {
299                return !isReadOnly;
300            }
301
302            @Override
303            public void editingStopped(ChangeEvent e) {
304                int row = getEditingRow();
305                int col = getEditingColumn();
306                super.editingStopped(e);
307
308                Object source = e.getSource();
309
310                if (source instanceof CellEditor) {
311                    CellEditor editor = (CellEditor) source;
312                    String cellValue = (String) editor.getCellEditorValue();
313                    text[row] = cellValue;
314                } // if (source instanceof CellEditor)
315            }
316        };
317        theTable.setName("TextView");
318
319        return theTable;
320    }
321
322    public void keyPressed(KeyEvent e) {
323    }
324
325    public void keyReleased(KeyEvent e) {
326    }
327
328    public void keyTyped(KeyEvent e) {
329        isTextChanged = true;
330    }
331
332    private JMenuBar createMenuBar() {
333        JMenuBar bar = new JMenuBar();
334        JMenu menu = new JMenu("Text", false);
335        menu.setMnemonic('T');
336        bar.add(menu);
337
338        JMenuItem item = new JMenuItem("Save To Text File");
339        // item.setMnemonic(KeyEvent.VK_T);
340        item.addActionListener(this);
341        item.setActionCommand("Save to text file");
342        menu.add(item);
343
344        menu.addSeparator();
345
346        item = new JMenuItem("Save Changes");
347        item.addActionListener(this);
348        item.setActionCommand("Save changes");
349        menu.add(item);
350
351        menu.addSeparator();
352
353        menu.addSeparator();
354
355        item = new JMenuItem("Close");
356        item.addActionListener(this);
357        item.setActionCommand("Close");
358        menu.add(item);
359
360        return bar;
361    }
362
363    /**
364     * Update dataset value in file. The change will go to file.
365     */
366    public void updateValueInFile() {
367        if (isReadOnly) {
368            return;
369        }
370
371        if (!(dataset instanceof ScalarDS)) {
372            return;
373        }
374
375        if (!isTextChanged) {
376            return;
377        }
378
379        int row = table.getEditingRow();
380        if (row >= 0) {
381            // make sure to update the current row
382            String cellValue = (String) textEditor.getCellEditorValue();
383            text[row] = cellValue;
384        }
385
386        try {
387            dataset.write();
388        }
389        catch (Exception ex) {
390            JOptionPane.showMessageDialog(this, ex, getTitle(),
391                    JOptionPane.ERROR_MESSAGE);
392            return;
393        }
394        isTextChanged = false;
395    }
396
397    /** Save data as text. */
398    private void saveAsText() throws Exception {
399        final JFileChooser fchooser = new JFileChooser(dataset.getFile());
400        fchooser.setFileFilter(DefaultFileFilter.getFileFilterText());
401        fchooser.changeToParentDirectory();
402        fchooser.setDialogTitle("Save Current Data To Text File --- "
403                + dataset.getName());
404
405        File choosedFile = new File(dataset.getName() + ".txt");
406        fchooser.setSelectedFile(choosedFile);
407        int returnVal = fchooser.showSaveDialog(this);
408
409        if (returnVal != JFileChooser.APPROVE_OPTION) {
410            return;
411        }
412
413        choosedFile = fchooser.getSelectedFile();
414        if (choosedFile == null) {
415            return;
416        }
417
418        String fname = choosedFile.getAbsolutePath();
419
420        // check if the file is in use
421        List fileList = viewer.getTreeView().getCurrentFiles();
422        if (fileList != null) {
423            FileFormat theFile = null;
424            Iterator iterator = fileList.iterator();
425            while (iterator.hasNext()) {
426                theFile = (FileFormat) iterator.next();
427                if (theFile.getFilePath().equals(fname)) {
428                    JOptionPane.showMessageDialog(this,
429                            "Unable to save data to file \"" + fname
430                                    + "\". \nThe file is being used.",
431                            getTitle(), JOptionPane.ERROR_MESSAGE);
432                    return;
433                }
434            }
435        }
436
437        if (choosedFile.exists()) {
438            int newFileFlag = JOptionPane.showConfirmDialog(this,
439                    "File exists. Do you want to replace it ?",
440                    this.getTitle(), JOptionPane.YES_NO_OPTION);
441            if (newFileFlag == JOptionPane.NO_OPTION) {
442                return;
443            }
444        }
445
446        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(
447                choosedFile)));
448
449        int rows = text.length;
450        for (int i = 0; i < rows; i++) {
451            out.print(text[i].trim());
452            out.println();
453            out.println();
454        }
455
456        out.flush();
457        out.close();
458
459        viewer.showStatus("Data save to: " + fname);
460
461        try {
462            RandomAccessFile rf = new RandomAccessFile(choosedFile, "r");
463            long size = rf.length();
464            rf.close();
465            viewer.showStatus("File size (bytes): " + size);
466        }
467        catch (Exception ex) {
468            log.debug("raf file size:", ex);
469        }
470    }
471
472    @Override
473    public void dispose() {
474        if (isTextChanged && !isReadOnly) {
475            int op = JOptionPane.showConfirmDialog(this, "\""
476                    + dataset.getName() + "\" has changed.\n"
477                    + "Do you want to save the changes?", getTitle(),
478                    JOptionPane.YES_NO_OPTION);
479
480            if (op == JOptionPane.YES_OPTION) {
481                updateValueInFile();
482            }
483        }
484
485        viewer.removeDataView(this);
486
487        super.dispose();
488    }
489
490    // Implementing DataView.
491    public HObject getDataObject() {
492        return dataset;
493    }
494
495    // Implementing TextView.
496    public String[] getText() {
497        return text;
498    }
499
500    // print the table
501    private void print() {
502        StreamPrintServiceFactory[] spsf = StreamPrintServiceFactory
503                .lookupStreamPrintServiceFactories(null, null);
504        for (int i = 0; i < spsf.length; i++) {
505            System.out.println(spsf[i]);
506        }
507        DocFlavor[] docFlavors = spsf[0].getSupportedDocFlavors();
508        for (int i = 0; i < docFlavors.length; i++) {
509            System.out.println(docFlavors[i]);
510        }
511
512        // TODO: windows url
513        // Get a text DocFlavor
514        InputStream is = null;
515        try {
516            is = new BufferedInputStream(new java.io.FileInputStream(
517                    "e:\\temp\\t.html"));
518        }
519        catch (Exception ex) {
520            log.debug("Get a text DocFlavor:", ex);
521        }
522        DocFlavor flavor = DocFlavor.STRING.TEXT_HTML;
523
524        // Get all available print services
525        PrintService[] services = PrintServiceLookup.lookupPrintServices(null,
526                null);
527
528        // Print this job on the first print server
529        DocPrintJob job = services[0].createPrintJob();
530        Doc doc = new SimpleDoc(is, flavor, null);
531
532        // Print it
533        try {
534            job.print(doc, null);
535        }
536        catch (Exception ex) {
537            System.out.println(ex);
538        }
539    }
540
541    private class TextAreaRenderer extends JTextArea implements
542            TableCellRenderer {
543        private static final long serialVersionUID = -5869975162678521978L;
544
545        private final DefaultTableCellRenderer adaptee = new DefaultTableCellRenderer();
546
547        /** map from table to map of rows to map of column heights */
548        private final Map cellSizes = new HashMap();
549
550        public TextAreaRenderer() {
551            setLineWrap(true);
552            setWrapStyleWord(true);
553        }
554
555        public Component getTableCellRendererComponent(
556                //
557                JTable table, Object obj, boolean isSelected, boolean hasFocus,
558                int row, int column) {
559            // set the colours, etc. using the standard for that platform
560            adaptee.getTableCellRendererComponent(table, obj, isSelected,
561                    hasFocus, row, column);
562            setForeground(adaptee.getForeground());
563            setBackground(adaptee.getBackground());
564            setBorder(adaptee.getBorder());
565            setFont(adaptee.getFont());
566            setText(adaptee.getText());
567
568            // This line was very important to get it working with JDK1.4
569            TableColumnModel columnModel = table.getColumnModel();
570            setSize(columnModel.getColumn(column).getWidth(), 100000);
571            int height_wanted = (int) getPreferredSize().getHeight();
572            addSize(table, row, column, height_wanted);
573            height_wanted = findTotalMaximumRowSize(table, row);
574            if (height_wanted != table.getRowHeight(row)) {
575                table.setRowHeight(row, height_wanted);
576                rowHeaders.setRowHeight(row, height_wanted);
577
578            }
579            return this;
580        }
581
582        private void addSize(JTable table, int row, int column, int height) {
583            Map rows = (Map) cellSizes.get(table);
584            if (rows == null) {
585                cellSizes.put(table, rows = new HashMap());
586            }
587            Map rowheights = (Map) rows.get(new Integer(row));
588            if (rowheights == null) {
589                rows.put(new Integer(row), rowheights = new HashMap());
590            }
591            rowheights.put(new Integer(column), new Integer(height));
592        }
593
594        /**
595         * Look through all columns and get the renderer. If it is also a
596         * TextAreaRenderer, we look at the maximum height in its hash table for
597         * this row.
598         */
599        private int findTotalMaximumRowSize(JTable table, int row) {
600            int maximum_height = 0;
601            Enumeration columns = table.getColumnModel().getColumns();
602            while (columns.hasMoreElements()) {
603                TableColumn tc = (TableColumn) columns.nextElement();
604                TableCellRenderer cellRenderer = tc.getCellRenderer();
605                if (cellRenderer instanceof TextAreaRenderer) {
606                    TextAreaRenderer tar = (TextAreaRenderer) cellRenderer;
607                    maximum_height = Math.max(maximum_height, tar
608                            .findMaximumRowSize(table, row));
609                }
610            }
611            return maximum_height;
612        }
613
614        private int findMaximumRowSize(JTable table, int row) {
615            Map rows = (Map) cellSizes.get(table);
616            if (rows == null) {
617                return 0;
618            }
619            Map rowheights = (Map) rows.get(new Integer(row));
620            if (rowheights == null) {
621                return 0;
622            }
623            int maximum_height = 0;
624            for (Iterator it = rowheights.entrySet().iterator(); it.hasNext();) {
625                Map.Entry entry = (Map.Entry) it.next();
626                int cellHeight = ((Integer) entry.getValue()).intValue();
627                maximum_height = Math.max(maximum_height, cellHeight);
628            }
629            return maximum_height;
630        }
631    }
632
633    private class TextAreaEditor extends DefaultCellEditor {
634        private static final long serialVersionUID = 1721646779892184957L;
635
636        public TextAreaEditor(KeyListener keyListener) {
637            super(new JTextField());
638
639            final JTextArea textArea = new JTextArea();
640
641            textArea.addKeyListener(keyListener);
642            textArea.setWrapStyleWord(true);
643            textArea.setLineWrap(true);
644            JScrollPane scrollPane = new JScrollPane(textArea);
645            scrollPane.setBorder(null);
646            editorComponent = scrollPane;
647            delegate = new DefaultCellEditor.EditorDelegate() {
648                private static final long serialVersionUID = 7662356579385373160L;
649
650                @Override
651                public void setValue(Object value) {
652                    textArea.setText((value != null) ? value.toString() : "");
653                }
654
655                @Override
656                public Object getCellEditorValue() {
657                    return textArea.getText();
658                }
659            };
660        }
661    }
662
663    /** RowHeader defines the row header component of the Spreadsheet. */
664    private class RowHeader extends JTable {
665        private static final long serialVersionUID = 2572539746584274419L;
666        private int currentRowIndex = -1;
667        private int lastRowIndex = -1;
668        private JTable parentTable;
669
670        public RowHeader(JTable pTable, Dataset dset) {
671            // Create a JTable with the same number of rows as
672            // the parent table and one column.
673            super(pTable.getRowCount(), 1);
674
675            long[] startArray = dset.getStartDims();
676            long[] strideArray = dset.getStride();
677            int[] selectedIndex = dset.getSelectedIndex();
678            int start = (int) startArray[selectedIndex[0]];
679            int stride = (int) strideArray[selectedIndex[0]];
680
681            // Store the parent table.
682            parentTable = pTable;
683
684            // Set the values of the row headers starting at 0.
685            int n = parentTable.getRowCount();
686            for (int i = 0; i < n; i++) {
687                setValueAt(new Integer(start + indexBase+ i * stride), i, 0);
688            }
689
690            // Get the only table column.
691            TableColumn col = getColumnModel().getColumn(0);
692
693            // Use the cell renderer in the column.
694            col.setCellRenderer(new RowHeaderRenderer());
695        }
696
697        /** Overridden to return false since the headers are not editable. */
698        @Override
699        public boolean isCellEditable(int row, int col) {
700            return false;
701        }
702
703        /** This is called when the selection changes in the row headers. */
704        @Override
705        public void valueChanged(ListSelectionEvent e) {
706            if (parentTable == null) {
707                return;
708            }
709
710            int rows[] = getSelectedRows();
711            if ((rows == null) || (rows.length == 0)) {
712                return;
713            }
714
715            parentTable.clearSelection();
716            parentTable.setRowSelectionInterval(rows[0], rows[rows.length - 1]);
717            parentTable.setColumnSelectionInterval(0, parentTable
718                    .getColumnCount() - 1);
719        }
720
721        @Override
722        protected void processMouseMotionEvent(MouseEvent e) {
723            if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
724                int colEnd = rowAtPoint(e.getPoint());
725
726                if (colEnd < 0) {
727                    colEnd = 0;
728                }
729                if (currentRowIndex < 0) {
730                    currentRowIndex = 0;
731                }
732
733                parentTable.clearSelection();
734
735                if (colEnd > currentRowIndex) {
736                    parentTable
737                            .setRowSelectionInterval(currentRowIndex, colEnd);
738                }
739                else {
740                    parentTable
741                            .setRowSelectionInterval(colEnd, currentRowIndex);
742                }
743
744                parentTable.setColumnSelectionInterval(0, parentTable
745                        .getColumnCount() - 1);
746            }
747        }
748
749        @Override
750        protected void processMouseEvent(MouseEvent e) {
751            int mouseID = e.getID();
752
753            if (mouseID == MouseEvent.MOUSE_CLICKED) {
754                if (currentRowIndex < 0) {
755                    return;
756                }
757
758                if (e.isControlDown()) {
759                    // select discontinguous rows
760                    parentTable.addRowSelectionInterval(currentRowIndex,
761                            currentRowIndex);
762                }
763                else if (e.isShiftDown()) {
764                    // select continguous columns
765                    if (lastRowIndex < 0) {
766                        parentTable.addRowSelectionInterval(0, currentRowIndex);
767                    }
768                    else if (lastRowIndex < currentRowIndex) {
769                        parentTable.addRowSelectionInterval(lastRowIndex,
770                                currentRowIndex);
771                    }
772                    else {
773                        parentTable.addRowSelectionInterval(currentRowIndex,
774                                lastRowIndex);
775                    }
776                }
777                else {
778                    // clear old selection and set new column selection
779                    parentTable.clearSelection();
780                    parentTable.setRowSelectionInterval(currentRowIndex,
781                            currentRowIndex);
782                }
783
784                lastRowIndex = currentRowIndex;
785
786                parentTable.setColumnSelectionInterval(0, parentTable
787                        .getColumnCount() - 1);
788            }
789            else if (mouseID == MouseEvent.MOUSE_PRESSED) {
790                currentRowIndex = rowAtPoint(e.getPoint());
791            }
792        }
793    } // private class RowHeader extends JTable
794
795    /**
796     * RowHeaderRenderer is a custom cell renderer that displays cells as
797     * buttons.
798     */
799    private class RowHeaderRenderer extends JLabel implements TableCellRenderer {
800        private static final long serialVersionUID = 3081275694689434654L;
801
802        public RowHeaderRenderer() {
803            super();
804            setHorizontalAlignment(SwingConstants.CENTER);
805
806            setOpaque(true);
807            setBorder(UIManager.getBorder("TableHeader.cellBorder"));
808            setBackground(Color.lightGray);
809        }
810
811        /** Configures the button for the current cell, and returns it. */
812        public Component getTableCellRendererComponent(JTable table,
813                Object value, boolean isSelected, boolean hasFocus, int row,
814                int column) {
815            setFont(table.getFont());
816
817            if (value != null) {
818                setText(value.toString());
819            }
820
821            return this;
822        }
823    } // private class RowHeaderRenderer extends JLabel implements
824      // TableCellRenderer
825
826}