source: src/main/java/weka/gui/arffviewer/ArffTable.java @ 4

Last change on this file since 4 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 12.5 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 * ArffTable.java
19 * Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui.arffviewer;
24
25import weka.core.Attribute;
26import weka.core.Instances;
27import weka.gui.ComponentHelper;
28import weka.gui.JTableHelper;
29import weka.gui.ViewerDialog;
30
31import java.awt.Component;
32import java.awt.event.ActionEvent;
33import java.awt.event.ActionListener;
34import java.awt.datatransfer.StringSelection;
35import java.util.Enumeration;
36import java.util.HashSet;
37import java.util.Iterator;
38
39import javax.swing.AbstractCellEditor;
40import javax.swing.DefaultCellEditor;
41import javax.swing.JButton;
42import javax.swing.JComboBox;
43import javax.swing.JOptionPane;
44import javax.swing.JTable;
45import javax.swing.event.ChangeEvent;
46import javax.swing.event.ChangeListener;
47import javax.swing.event.TableModelEvent;
48import javax.swing.table.TableCellEditor;
49import javax.swing.table.TableModel;
50
51/**
52 * A specialized JTable for the Arff-Viewer.
53 *
54 *
55 * @author FracPete (fracpete at waikato dot ac dot nz)
56 * @version $Revision: 1.8 $
57 */
58public class ArffTable
59  extends JTable {
60 
61  /** for serialization */
62  static final long serialVersionUID = -2016200506908637967L;
63
64  /**
65   * a special Editor for editing the relation attribute.
66   */
67  protected class RelationalCellEditor
68    extends AbstractCellEditor
69    implements TableCellEditor {
70
71    /** for serialization */
72    private static final long serialVersionUID = 657969163293205963L;
73   
74    /** the button for opening the dialog */
75    protected JButton m_Button;
76   
77    /** the current instances */
78    protected Instances m_CurrentInst;
79   
80    /** the row index this editor is for */
81    protected int m_RowIndex;
82   
83    /** the column index this editor is for */
84    protected int m_ColumnIndex;
85   
86    /**
87     * initializes the editor
88     *
89     * @param rowIndex          the row index
90     * @param columnIndex       the column index
91     */
92    public RelationalCellEditor(int rowIndex, int columnIndex) {
93      super();
94
95      m_CurrentInst = getInstancesAt(rowIndex, columnIndex);
96      m_RowIndex    = rowIndex;
97      m_ColumnIndex = columnIndex;
98     
99      m_Button = new JButton("...");
100      m_Button.addActionListener(new ActionListener() {
101        public void actionPerformed(ActionEvent evt) {
102          ViewerDialog        dialog;
103          int                 result;
104         
105          dialog = new ViewerDialog(null);
106          dialog.setTitle(
107              "Relational attribute Viewer - " 
108              + ((ArffSortedTableModel) getModel()).getInstances().attribute(m_ColumnIndex - 1).name());
109          result = dialog.showDialog(m_CurrentInst);
110          if (result == ViewerDialog.APPROVE_OPTION) {
111            m_CurrentInst = dialog.getInstances();
112            fireEditingStopped();
113          }
114          else {
115            fireEditingCanceled();
116          }
117        }
118      });
119    }
120
121    /**
122     * returns the underlying instances at the given position
123     *
124     * @param rowIndex          the row index
125     * @param columnIndex       the column index
126     * @return                  the corresponding instances
127     */
128    protected Instances getInstancesAt(int rowIndex, int columnIndex) {
129      Instances                 result;
130      ArffSortedTableModel      model;
131      double                    value;
132     
133      model = (ArffSortedTableModel) getModel();
134      value = model.getInstancesValueAt(rowIndex, columnIndex);
135      result = model.getInstances().attribute(columnIndex - 1).relation((int) value);
136     
137      return result;
138    }
139   
140    /**
141     * Sets an initial value for the editor. This will cause the editor to
142     * stopEditing and lose any partially edited value if the editor is
143     * editing when this method is called.
144     *
145     * @param table             the table this editor belongs to
146     * @param value             the value to edit
147     * @param isSelected        whether the cell is selected
148     * @param row               the row index
149     * @param column            the column index
150     * @return                  the
151     */
152    public Component getTableCellEditorComponent(JTable table,
153                                                 Object value,
154                                                 boolean isSelected,
155                                                 int row,
156                                                 int column) {
157      return m_Button;
158    }
159
160    /**
161     * Returns the value contained in the editor.
162     *
163     * @return          the value contained in the editor
164     */
165    public Object getCellEditorValue() {
166      return m_CurrentInst;
167    }
168  }
169 
170  /** the search string */
171  private String m_SearchString;
172  /** the listeners for changes */
173  private HashSet m_ChangeListeners;
174 
175  /**
176   * initializes with no model
177   */
178  public ArffTable() {
179    this(new ArffSortedTableModel(""));
180  }
181 
182  /**
183   * initializes with the given model
184   *
185   * @param model               the model to use
186   */
187  public ArffTable(TableModel model) {
188    super(model);
189   
190    setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
191  }
192 
193  /**
194   * sets the new model
195   *
196   * @param model               the model to use
197   */
198  public void setModel(TableModel model) {
199    ArffSortedTableModel      arffModel;
200   
201    // initialize the search
202    m_SearchString = null;
203   
204    // init the listeners
205    if (m_ChangeListeners == null)
206      m_ChangeListeners = new HashSet();
207   
208    super.setModel(model);
209   
210    if (model == null)
211      return;
212   
213    if (!(model instanceof ArffSortedTableModel))
214      return;
215   
216    arffModel = (ArffSortedTableModel) model;
217    arffModel.addMouseListenerToHeader(this);
218    arffModel.addTableModelListener(this);
219    arffModel.sort(0);
220    setLayout();
221    setSelectedColumn(0);
222   
223    // disable column moving
224    if (getTableHeader() != null)
225      getTableHeader().setReorderingAllowed(false);
226  }
227
228  /**
229   * returns the cell editor for the given cell
230   *
231   * @param row         the row index
232   * @param column      the column index
233   * @return            the cell editor
234   */
235  public TableCellEditor getCellEditor(int row, int column) {
236    TableCellEditor             result;
237   
238    // relational attribute?
239    if (    (getModel() instanceof ArffSortedTableModel) 
240         && (((ArffSortedTableModel) getModel()).getType(column) == Attribute.RELATIONAL) )
241      result = new RelationalCellEditor(row, column);
242    // default
243    else
244      result = super.getCellEditor(row, column);
245   
246    return result;
247  }
248
249  /**
250   * returns whether the model is read-only
251   *
252   * @return            true if model is read-only
253   */
254  public boolean isReadOnly() {
255    return ((ArffSortedTableModel) getModel()).isReadOnly();
256  }
257 
258  /**
259   * sets whether the model is read-only
260   *
261   * @param value       if true the model is set to read-only
262   */
263  public void setReadOnly(boolean value) {
264    ((ArffSortedTableModel) getModel()).setReadOnly(value);
265  }
266 
267  /**
268   * sets the cell renderer and calcs the optimal column width
269   */
270  private void setLayout() {
271    ArffSortedTableModel      arffModel;
272    int                  i;
273    JComboBox            combo;
274    Enumeration          enm;
275   
276    arffModel = (ArffSortedTableModel) getModel();
277   
278    for (i = 0; i < getColumnCount(); i++) {
279      // optimal colwidths (only according to header!)
280      JTableHelper.setOptimalHeaderWidth(this, i);
281     
282      // CellRenderer
283      getColumnModel().getColumn(i).setCellRenderer(
284          new ArffTableCellRenderer());
285     
286      // CellEditor
287      if (i > 0) {
288        if (arffModel.getType(i) == Attribute.NOMINAL) {
289          combo = new JComboBox();
290          combo.addItem(null);
291          enm  = arffModel.getInstances().attribute(i - 1).enumerateValues();
292          while (enm.hasMoreElements())
293            combo.addItem(enm.nextElement());
294          getColumnModel().getColumn(i).setCellEditor(new DefaultCellEditor(combo));
295        }
296        else {
297          getColumnModel().getColumn(i).setCellEditor(null);
298        }
299      }
300    }
301  }
302 
303  /**
304   * returns the basically the attribute name of the column and not the
305   * HTML column name via getColumnName(int)
306   *
307   * @param columnIndex         the column index
308   * @return                    the plain name
309   */
310  public String getPlainColumnName(int columnIndex) {
311    ArffSortedTableModel      arffModel;
312    String               result;
313   
314    result = "";
315   
316    if (getModel() == null)
317      return result;
318    if (!(getModel() instanceof ArffSortedTableModel)) 
319      return result;
320   
321    arffModel = (ArffSortedTableModel) getModel();
322   
323    if ( (columnIndex >= 0) && (columnIndex < getColumnCount()) ) {
324      if (columnIndex == 0)
325        result = "No.";
326      else
327        result = arffModel.getAttributeAt(columnIndex).name();
328    }
329   
330    return result;
331  }
332 
333  /**
334   * returns the selected content in a StringSelection that can be copied to
335   * the clipboard and used in Excel, if nothing is selected the whole table
336   * is copied to the clipboard
337   *
338   * @return                    the current selection
339   */
340  public StringSelection getStringSelection() {
341    StringSelection         result;
342    int[]                   indices;
343    int                     i;
344    int                     n;
345    StringBuffer            tmp;
346   
347    result = null;
348   
349    // nothing selected? -> all
350    if (getSelectedRow() == -1) {
351      // really?
352      if (ComponentHelper.showMessageBox(
353            getParent(),
354            "Question...",
355            "Do you really want to copy the whole table?",
356            JOptionPane.YES_NO_OPTION,
357            JOptionPane.QUESTION_MESSAGE ) != JOptionPane.YES_OPTION)
358        return result;
359     
360      indices = new int[getRowCount()];
361      for (i = 0; i < indices.length; i++)
362        indices[i] = i;
363    }
364    else {
365      indices = getSelectedRows();
366    }
367   
368    // get header
369    tmp = new StringBuffer();
370    for (i = 0; i < getColumnCount(); i++) {
371      if (i > 0)
372        tmp.append("\t");
373      tmp.append(getPlainColumnName(i));
374    }
375    tmp.append("\n");
376   
377    // get content
378    for (i = 0; i < indices.length; i++) {
379      for (n = 0; n < getColumnCount(); n++) {
380        if (n > 0)
381          tmp.append("\t");
382        tmp.append(getValueAt(indices[i], n).toString());
383      }
384      tmp.append("\n");
385    }
386   
387    result = new StringSelection(tmp.toString());
388   
389    return result;
390  }
391 
392  /**
393   * sets the search string to look for in the table, NULL or "" disables
394   * the search
395   *
396   * @param searchString        the search string to use
397   */
398  public void setSearchString(String searchString) {
399    this.m_SearchString = searchString;
400    repaint();
401  }
402 
403  /**
404   * returns the search string, can be NULL if no search string is set
405   *
406   * @return                    the current search string
407   */
408  public String getSearchString() {
409    return m_SearchString;
410  }
411 
412  /**
413   * sets the selected column
414   *
415   * @param index               the column to select
416   */
417  public void setSelectedColumn(int index) {
418    getColumnModel().getSelectionModel().clearSelection();
419    getColumnModel().getSelectionModel().setSelectionInterval(index, index);
420    resizeAndRepaint();
421    if (getTableHeader() != null)
422      getTableHeader().resizeAndRepaint();
423  }
424 
425  /**
426   * This fine grain notification tells listeners the exact range of cells,
427   * rows, or columns that changed.
428   *
429   * @param e           the table event
430   */
431  public void tableChanged(TableModelEvent e) {
432    super.tableChanged(e);
433   
434    setLayout();
435    notifyListener();
436  }
437 
438  /**
439   * notfies all listener of the change
440   */
441  private void notifyListener() {
442    Iterator                iter;
443   
444    iter = m_ChangeListeners.iterator();
445    while (iter.hasNext())
446      ((ChangeListener) iter.next()).stateChanged(new ChangeEvent(this));
447  }
448 
449  /**
450   * Adds a ChangeListener to the panel
451   *
452   * @param l                   the listener to add
453   */
454  public void addChangeListener(ChangeListener l) {
455    m_ChangeListeners.add(l);
456  }
457 
458  /**
459   * Removes a ChangeListener from the panel
460   *
461   * @param l                   the listener to remove
462   */
463  public void removeChangeListener(ChangeListener l) {
464    m_ChangeListeners.remove(l);
465  }
466}
Note: See TracBrowser for help on using the repository browser.