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.
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.
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.
19
* Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
23
package weka.gui.arffviewer;
25
import weka.core.Instances;
26
import weka.core.Undoable;
27
import weka.core.Utils;
28
import weka.gui.ComponentHelper;
29
import weka.gui.JTableHelper;
30
import weka.gui.ListSelectorDialog;
32
import java.awt.BorderLayout;
33
import java.awt.Cursor;
34
import java.awt.Toolkit;
35
import java.awt.datatransfer.Clipboard;
36
import java.awt.datatransfer.StringSelection;
37
import java.awt.event.ActionEvent;
38
import java.awt.event.ActionListener;
39
import java.awt.event.MouseEvent;
40
import java.awt.event.MouseListener;
42
import java.util.Collections;
43
import java.util.HashSet;
44
import java.util.Iterator;
45
import java.util.Vector;
47
import javax.swing.JLabel;
48
import javax.swing.JList;
49
import javax.swing.JMenuItem;
50
import javax.swing.JOptionPane;
51
import javax.swing.JPanel;
52
import javax.swing.JPopupMenu;
53
import javax.swing.JScrollPane;
54
import javax.swing.event.ChangeEvent;
55
import javax.swing.event.ChangeListener;
56
import javax.swing.event.TableModelEvent;
59
* A Panel representing an ARFF-Table and the associated filename.
62
* @author FracPete (fracpete at waikato dot ac dot nz)
63
* @version $Revision: 1.8 $
66
public class ArffPanel
68
implements ActionListener, ChangeListener, MouseListener, Undoable {
70
/** for serialization */
71
static final long serialVersionUID = -4697041150989513939L;
73
/** the name of the tab for instances that were set directly */
74
public final static String TAB_INSTANCES = "Instances";
76
/** the underlying table */
77
private ArffTable m_TableArff;
78
/** the popup menu for the header row */
79
private JPopupMenu m_PopupHeader;
80
/** the popup menu for the data rows */
81
private JPopupMenu m_PopupRows;
82
/** displays the relation name */
83
private JLabel m_LabelName;
86
private JMenuItem menuItemMean;
87
private JMenuItem menuItemSetAllValues;
88
private JMenuItem menuItemSetMissingValues;
89
private JMenuItem menuItemReplaceValues;
90
private JMenuItem menuItemRenameAttribute;
91
private JMenuItem menuItemAttributeAsClass;
92
private JMenuItem menuItemDeleteAttribute;
93
private JMenuItem menuItemDeleteAttributes;
94
private JMenuItem menuItemSortInstances;
95
private JMenuItem menuItemDeleteSelectedInstance;
96
private JMenuItem menuItemDeleteAllSelectedInstances;
97
private JMenuItem menuItemSearch;
98
private JMenuItem menuItemClearSearch;
99
private JMenuItem menuItemUndo;
100
private JMenuItem menuItemCopy;
101
private JMenuItem menuItemOptimalColWidth;
102
private JMenuItem menuItemOptimalColWidths;
104
/** the filename used in the title */
105
private String m_Filename;
106
/** the title prefix */
107
private String m_Title;
108
/** the currently selected column */
109
private int m_CurrentCol;
110
/** flag for whether data got changed */
111
private boolean m_Changed;
112
/** the listeners that listen for modifications */
113
private HashSet m_ChangeListeners;
114
/** the string used in the last search */
115
private String m_LastSearch;
116
/** the string used in the last replace */
117
private String m_LastReplace;
120
* initializes the panel with no data
130
* initializes the panel and loads the specified file
132
* @param filename the file to load
134
public ArffPanel(String filename) {
141
* initializes the panel with the given data
143
* @param data the data to use
145
public ArffPanel(Instances data) {
154
* any member variables are initialized here
156
protected void initialize() {
163
m_ChangeListeners = new HashSet();
167
* creates all the components in the frame
169
protected void createPanel() {
172
setLayout(new BorderLayout());
174
menuItemMean = new JMenuItem("Get mean...");
175
menuItemMean.addActionListener(this);
176
menuItemSetAllValues = new JMenuItem("Set all values to...");
177
menuItemSetAllValues.addActionListener(this);
178
menuItemSetMissingValues = new JMenuItem("Set missing values to...");
179
menuItemSetMissingValues.addActionListener(this);
180
menuItemReplaceValues = new JMenuItem("Replace values with...");
181
menuItemReplaceValues.addActionListener(this);
182
menuItemRenameAttribute = new JMenuItem("Rename attribute...");
183
menuItemRenameAttribute.addActionListener(this);
184
menuItemAttributeAsClass = new JMenuItem("Attribute as class");
185
menuItemAttributeAsClass.addActionListener(this);
186
menuItemDeleteAttribute = new JMenuItem("Delete attribute");
187
menuItemDeleteAttribute.addActionListener(this);
188
menuItemDeleteAttributes = new JMenuItem("Delete attributes...");
189
menuItemDeleteAttributes.addActionListener(this);
190
menuItemSortInstances = new JMenuItem("Sort data (ascending)");
191
menuItemSortInstances.addActionListener(this);
192
menuItemOptimalColWidth = new JMenuItem("Optimal column width (current)");
193
menuItemOptimalColWidth.addActionListener(this);
194
menuItemOptimalColWidths = new JMenuItem("Optimal column width (all)");
195
menuItemOptimalColWidths.addActionListener(this);
198
menuItemUndo = new JMenuItem("Undo");
199
menuItemUndo.addActionListener(this);
200
menuItemCopy = new JMenuItem("Copy");
201
menuItemCopy.addActionListener(this);
202
menuItemSearch = new JMenuItem("Search...");
203
menuItemSearch.addActionListener(this);
204
menuItemClearSearch = new JMenuItem("Clear search");
205
menuItemClearSearch.addActionListener(this);
206
menuItemDeleteSelectedInstance = new JMenuItem("Delete selected instance");
207
menuItemDeleteSelectedInstance.addActionListener(this);
208
menuItemDeleteAllSelectedInstances = new JMenuItem("Delete ALL selected instances");
209
menuItemDeleteAllSelectedInstances.addActionListener(this);
212
m_TableArff = new ArffTable();
213
m_TableArff.setToolTipText("Right click (or left+alt) for context menu");
214
m_TableArff.getTableHeader().addMouseListener(this);
215
m_TableArff.getTableHeader().setToolTipText("<html><b>Sort view:</b> left click = ascending / Shift + left click = descending<br><b>Menu:</b> right click (or left+alt)</html>");
216
m_TableArff.getTableHeader().setDefaultRenderer(new ArffTableCellRenderer());
217
m_TableArff.addChangeListener(this);
218
m_TableArff.addMouseListener(this);
219
pane = new JScrollPane(m_TableArff);
220
add(pane, BorderLayout.CENTER);
223
m_LabelName = new JLabel();
224
add(m_LabelName, BorderLayout.NORTH);
228
* initializes the popup menus
230
private void initPopupMenus() {
232
m_PopupHeader = new JPopupMenu();
233
m_PopupHeader.addMouseListener(this);
234
m_PopupHeader.add(menuItemMean);
236
m_PopupHeader.addSeparator();
237
m_PopupHeader.add(menuItemSetAllValues);
238
m_PopupHeader.add(menuItemSetMissingValues);
239
m_PopupHeader.add(menuItemReplaceValues);
240
m_PopupHeader.addSeparator();
241
m_PopupHeader.add(menuItemRenameAttribute);
242
m_PopupHeader.add(menuItemAttributeAsClass);
243
m_PopupHeader.add(menuItemDeleteAttribute);
244
m_PopupHeader.add(menuItemDeleteAttributes);
245
m_PopupHeader.add(menuItemSortInstances);
247
m_PopupHeader.addSeparator();
248
m_PopupHeader.add(menuItemOptimalColWidth);
249
m_PopupHeader.add(menuItemOptimalColWidths);
252
m_PopupRows = new JPopupMenu();
253
m_PopupRows.addMouseListener(this);
255
m_PopupRows.add(menuItemUndo);
256
m_PopupRows.addSeparator();
258
m_PopupRows.add(menuItemCopy);
259
m_PopupRows.addSeparator();
260
m_PopupRows.add(menuItemSearch);
261
m_PopupRows.add(menuItemClearSearch);
263
m_PopupRows.addSeparator();
264
m_PopupRows.add(menuItemDeleteSelectedInstance);
265
m_PopupRows.add(menuItemDeleteAllSelectedInstances);
270
* sets the enabled/disabled state of the menu items
272
private void setMenu() {
277
ArffSortedTableModel model;
280
model = (ArffSortedTableModel) m_TableArff.getModel();
281
isNull = (model.getInstances() == null);
282
hasColumns = !isNull && (model.getInstances().numAttributes() > 0);
283
hasRows = !isNull && (model.getInstances().numInstances() > 0);
284
attSelected = hasColumns && (m_CurrentCol > 0);
285
isNumeric = attSelected && (model.getAttributeAt(m_CurrentCol).isNumeric());
287
menuItemUndo.setEnabled(canUndo());
288
menuItemCopy.setEnabled(true);
289
menuItemSearch.setEnabled(true);
290
menuItemClearSearch.setEnabled(true);
291
menuItemMean.setEnabled(isNumeric);
292
menuItemSetAllValues.setEnabled(attSelected);
293
menuItemSetMissingValues.setEnabled(attSelected);
294
menuItemReplaceValues.setEnabled(attSelected);
295
menuItemRenameAttribute.setEnabled(attSelected);
296
menuItemDeleteAttribute.setEnabled(attSelected);
297
menuItemDeleteAttributes.setEnabled(attSelected);
298
menuItemSortInstances.setEnabled(hasRows && attSelected);
299
menuItemDeleteSelectedInstance.setEnabled(hasRows && m_TableArff.getSelectedRow() > -1);
300
menuItemDeleteAllSelectedInstances.setEnabled(hasRows && (m_TableArff.getSelectedRows().length > 0));
304
* returns the table component
308
public ArffTable getTable() {
313
* returns the title for the Tab, i.e. the filename
315
* @return the title for the tab
317
public String getTitle() {
322
* returns the filename
324
* @return the filename
326
public String getFilename() {
333
* @param filename the new filename
335
public void setFilename(String filename) {
336
m_Filename = filename;
341
* returns the instances of the panel, if none then NULL
343
* @return the instances of the panel
345
public Instances getInstances() {
350
if (m_TableArff.getModel() != null)
351
result = ((ArffSortedTableModel) m_TableArff.getModel()).getInstances();
357
* displays the given instances, i.e. creates a tab with the title
358
* TAB_INSTANCES. if one already exists it closes it.<br>
359
* if a different instances object is used here, don't forget to clear
360
* the undo-history by calling <code>clearUndo()</code>
362
* @param data the instances to display
363
* @see #TAB_INSTANCES
366
public void setInstances(Instances data) {
367
ArffSortedTableModel model;
369
m_Filename = TAB_INSTANCES;
372
model = new ArffSortedTableModel(data);
374
m_TableArff.setModel(model);
381
* returns a list with the attributes
383
* @return a list of the attributes
385
public Vector getAttributes() {
389
result = new Vector();
390
for (i = 0; i < getInstances().numAttributes(); i++)
391
result.add(getInstances().attribute(i).name());
392
Collections.sort(result);
398
* can only reset the changed state to FALSE
400
* @param changed if false, resets the changed state
402
public void setChanged(boolean changed) {
404
this.m_Changed = changed;
410
* returns whether the content of the panel was changed
412
* @return true if the content was changed
414
public boolean isChanged() {
419
* returns whether the model is read-only
421
* @return true if model is read-only
423
public boolean isReadOnly() {
424
if (m_TableArff == null)
427
return ((ArffSortedTableModel) m_TableArff.getModel()).isReadOnly();
431
* sets whether the model is read-only
433
* @param value if true the model is set to read-only
435
public void setReadOnly(boolean value) {
436
if (m_TableArff != null)
437
((ArffSortedTableModel) m_TableArff.getModel()).setReadOnly(value);
441
* returns whether undo support is enabled
443
* @return true if undo is enabled
445
public boolean isUndoEnabled() {
446
return ((ArffSortedTableModel) m_TableArff.getModel()).isUndoEnabled();
450
* sets whether undo support is enabled
452
* @param enabled whether to enable/disable undo support
454
public void setUndoEnabled(boolean enabled) {
455
((ArffSortedTableModel) m_TableArff.getModel()).setUndoEnabled(enabled);
459
* removes the undo history
461
public void clearUndo() {
462
((ArffSortedTableModel) m_TableArff.getModel()).clearUndo();
466
* returns whether an undo is possible
468
* @return true if undo is possible
470
public boolean canUndo() {
471
return ((ArffSortedTableModel) m_TableArff.getModel()).canUndo();
475
* performs an undo action
479
((ArffSortedTableModel) m_TableArff.getModel()).undo();
481
// notify about update
487
* adds the current state of the instances to the undolist
489
public void addUndoPoint() {
490
((ArffSortedTableModel) m_TableArff.getModel()).addUndoPoint();
497
* sets the title (i.e. filename)
499
private void createTitle() {
502
if (m_Filename.equals("")) {
505
else if (m_Filename.equals(TAB_INSTANCES)) {
506
m_Title = TAB_INSTANCES;
510
file = new File(m_Filename);
511
m_Title = file.getName();
513
catch (Exception e) {
523
* sets the relation name
525
private void createName() {
526
ArffSortedTableModel model;
528
model = (ArffSortedTableModel) m_TableArff.getModel();
529
if ((model != null) && (model.getInstances() != null))
530
m_LabelName.setText("Relation: " + model.getInstances().relationName());
532
m_LabelName.setText("");
536
* loads the specified file into the table
538
* @param filename the file to load
540
private void loadFile(String filename) {
541
ArffSortedTableModel model;
543
this.m_Filename = filename;
547
if (filename.equals(""))
550
model = new ArffSortedTableModel(filename);
552
m_TableArff.setModel(model);
558
* calculates the mean of the given numeric column
560
private void calcMean() {
561
ArffSortedTableModel model;
565
// no column selected?
566
if (m_CurrentCol == -1)
569
model = (ArffSortedTableModel) m_TableArff.getModel();
572
if (!model.getAttributeAt(m_CurrentCol).isNumeric())
576
for (i = 0; i < model.getRowCount(); i++)
577
mean += model.getInstances().instance(i).value(m_CurrentCol - 1);
578
mean = mean / model.getRowCount();
581
ComponentHelper.showMessageBox(
583
"Mean for attribute...",
584
"Mean for attribute '"
585
+ m_TableArff.getPlainColumnName(m_CurrentCol)
586
+ "':\n\t" + Utils.doubleToString(mean, 3),
587
JOptionPane.OK_CANCEL_OPTION,
588
JOptionPane.PLAIN_MESSAGE);
592
* sets the specified values in a column to a new value
594
* @param o the menu item
596
private void setValues(Object o) {
602
ArffSortedTableModel model;
607
if (o == menuItemSetMissingValues) {
608
title = "Replace missing values...";
609
msg = "New value for MISSING values";
611
else if (o == menuItemSetAllValues) {
612
title = "Set all values...";
613
msg = "New value for ALL values";
615
else if (o == menuItemReplaceValues) {
616
title = "Replace values...";
622
value = ComponentHelper.showInputBox(m_TableArff.getParent(), title, msg, m_LastSearch);
628
m_LastSearch = value;
631
if (o == menuItemReplaceValues) {
632
valueNew = ComponentHelper.showInputBox(m_TableArff.getParent(), title, "New value", m_LastReplace);
633
if (valueNew == null)
635
m_LastReplace = valueNew;
638
model = (ArffSortedTableModel) m_TableArff.getModel();
639
model.setNotificationEnabled(false);
643
model.setUndoEnabled(false);
646
for (i = 0; i < m_TableArff.getRowCount(); i++) {
647
if (o == menuItemSetAllValues)
648
model.setValueAt(value, i, m_CurrentCol);
650
if ( (o == menuItemSetMissingValues)
651
&& model.isMissingAt(i, m_CurrentCol) )
652
model.setValueAt(value, i, m_CurrentCol);
653
else if ( (o == menuItemReplaceValues)
654
&& model.getValueAt(i, m_CurrentCol).toString().equals(value) )
655
model.setValueAt(valueNew, i, m_CurrentCol);
657
model.setUndoEnabled(true);
658
model.setNotificationEnabled(true);
659
model.notifyListener(new TableModelEvent(model, 0, model.getRowCount(), m_CurrentCol, TableModelEvent.UPDATE));
662
m_TableArff.repaint();
666
* deletes the currently selected attribute
668
public void deleteAttribute() {
669
ArffSortedTableModel model;
671
// no column selected?
672
if (m_CurrentCol == -1)
675
model = (ArffSortedTableModel) m_TableArff.getModel();
677
// really an attribute column?
678
if (model.getAttributeAt(m_CurrentCol) == null)
682
if (ComponentHelper.showMessageBox(
685
"Do you really want to delete the attribute '"
686
+ model.getAttributeAt(m_CurrentCol).name() + "'?",
687
JOptionPane.YES_NO_OPTION,
688
JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION)
691
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
692
model.deleteAttributeAt(m_CurrentCol);
693
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
697
* deletes the chosen attributes
699
public void deleteAttributes() {
700
ListSelectorDialog dialog;
701
ArffSortedTableModel model;
708
list = new JList(getAttributes());
709
dialog = new ListSelectorDialog(null, list);
710
result = dialog.showDialog();
712
if (result != ListSelectorDialog.APPROVE_OPTION)
715
atts = list.getSelectedValues();
718
if (ComponentHelper.showMessageBox(
721
"Do you really want to delete these "
722
+ atts.length + " attributes?",
723
JOptionPane.YES_NO_OPTION,
724
JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION)
727
model = (ArffSortedTableModel) m_TableArff.getModel();
728
indices = new int[atts.length];
729
for (i = 0; i < atts.length; i++)
730
indices[i] = model.getAttributeColumn(atts[i].toString());
732
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
733
model.deleteAttributes(indices);
734
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
738
* sets the current attribute as class attribute, i.e. it moves it to the end
741
public void attributeAsClass() {
742
ArffSortedTableModel model;
744
// no column selected?
745
if (m_CurrentCol == -1)
748
model = (ArffSortedTableModel) m_TableArff.getModel();
750
// really an attribute column?
751
if (model.getAttributeAt(m_CurrentCol) == null)
754
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
755
model.attributeAsClassAt(m_CurrentCol);
756
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
760
* renames the current attribute
762
public void renameAttribute() {
763
ArffSortedTableModel model;
766
// no column selected?
767
if (m_CurrentCol == -1)
770
model = (ArffSortedTableModel) m_TableArff.getModel();
772
// really an attribute column?
773
if (model.getAttributeAt(m_CurrentCol) == null)
776
newName = ComponentHelper.showInputBox(getParent(), "Rename attribute...", "Enter new Attribute name", model.getAttributeAt(m_CurrentCol).name());
780
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
781
model.renameAttributeAt(m_CurrentCol, newName);
782
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
786
* deletes the currently selected instance
788
public void deleteInstance() {
791
index = m_TableArff.getSelectedRow();
795
((ArffSortedTableModel) m_TableArff.getModel()).deleteInstanceAt(index);
799
* deletes all the currently selected instances
801
public void deleteInstances() {
804
if (m_TableArff.getSelectedRow() == -1)
807
indices = m_TableArff.getSelectedRows();
808
((ArffSortedTableModel) m_TableArff.getModel()).deleteInstances(indices);
812
* sorts the instances via the currently selected column
814
public void sortInstances() {
815
if (m_CurrentCol == -1)
818
((ArffSortedTableModel) m_TableArff.getModel()).sortInstances(m_CurrentCol);
822
* copies the content of the selection to the clipboard
824
public void copyContent() {
825
StringSelection selection;
828
selection = getTable().getStringSelection();
829
if (selection == null)
832
clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
833
clipboard.setContents(selection, selection);
837
* searches for a string in the cells
839
public void search() {
843
searchString = ComponentHelper.showInputBox(getParent(), "Search...", "Enter the string to search for", m_LastSearch);
844
if (searchString != null)
845
m_LastSearch = searchString;
847
getTable().setSearchString(searchString);
851
* clears the search, i.e. resets the found cells
853
public void clearSearch() {
854
getTable().setSearchString("");
858
* calculates the optimal column width for the current column
860
public void setOptimalColWidth() {
861
// no column selected?
862
if (m_CurrentCol == -1)
865
JTableHelper.setOptimalColumnWidth(getTable(), m_CurrentCol);
869
* calculates the optimal column widths for all columns
871
public void setOptimalColWidths() {
872
JTableHelper.setOptimalColumnWidth(getTable());
876
* invoked when an action occurs
878
* @param e the action event
880
public void actionPerformed(ActionEvent e) {
885
if (o == menuItemMean)
887
else if (o == menuItemSetAllValues)
888
setValues(menuItemSetAllValues);
889
else if (o == menuItemSetMissingValues)
890
setValues(menuItemSetMissingValues);
891
else if (o == menuItemReplaceValues)
892
setValues(menuItemReplaceValues);
893
else if (o == menuItemRenameAttribute)
895
else if (o == menuItemAttributeAsClass)
897
else if (o == menuItemDeleteAttribute)
899
else if (o == menuItemDeleteAttributes)
901
else if (o == menuItemDeleteSelectedInstance)
903
else if (o == menuItemDeleteAllSelectedInstances)
905
else if (o == menuItemSortInstances)
907
else if (o == menuItemSearch)
909
else if (o == menuItemClearSearch)
911
else if (o == menuItemUndo)
913
else if (o == menuItemCopy)
915
else if (o == menuItemOptimalColWidth)
916
setOptimalColWidth();
917
else if (o == menuItemOptimalColWidths)
918
setOptimalColWidths();
922
* Invoked when a mouse button has been pressed and released on a component
924
* @param e the mouse event
926
public void mouseClicked(MouseEvent e) {
930
col = m_TableArff.columnAtPoint(e.getPoint());
931
popup = ((e.getButton() == MouseEvent.BUTTON3) && (e.getClickCount() == 1))
932
|| ((e.getButton() == MouseEvent.BUTTON1) && (e.getClickCount() == 1) && e.isAltDown() && !e.isControlDown() && !e.isShiftDown());
933
popup = popup && (getInstances() != null);
935
if (e.getSource() == m_TableArff.getTableHeader()) {
943
m_PopupHeader.show(e.getComponent(), e.getX(), e.getY());
946
else if (e.getSource() == m_TableArff) {
952
m_PopupRows.show(e.getComponent(), e.getX(), e.getY());
957
if ( (e.getButton() == MouseEvent.BUTTON1)
958
&& (e.getClickCount() == 1)
961
m_TableArff.setSelectedColumn(col);
966
* Invoked when the mouse enters a component.
968
* @param e the mouse event
970
public void mouseEntered(MouseEvent e) {
974
* Invoked when the mouse exits a component
976
* @param e the mouse event
978
public void mouseExited(MouseEvent e) {
982
* Invoked when a mouse button has been pressed on a component
984
* @param e the mouse event
986
public void mousePressed(MouseEvent e) {
990
* Invoked when a mouse button has been released on a component.
992
* @param e the mouse event
994
public void mouseReleased(MouseEvent e) {
998
* Invoked when the target of the listener has changed its state.
1000
* @param e the change event
1002
public void stateChanged(ChangeEvent e) {
1009
* notfies all listener of the change
1011
public void notifyListener() {
1014
iter = m_ChangeListeners.iterator();
1015
while (iter.hasNext())
1016
((ChangeListener) iter.next()).stateChanged(new ChangeEvent(this));
1020
* Adds a ChangeListener to the panel
1022
* @param l the listener to add
1024
public void addChangeListener(ChangeListener l) {
1025
m_ChangeListeners.add(l);
1029
* Removes a ChangeListener from the panel
1031
* @param l the listener to remove
1033
public void removeChangeListener(ChangeListener l) {
1034
m_ChangeListeners.remove(l);