0001 /*
0002 * Copyright (c) 1995-2010, The University of Sheffield. See the file
0003 * COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0004 *
0005 * This file is part of GATE (see http://gate.ac.uk/), and is free
0006 * software, licenced under the GNU Library General Public License,
0007 * Version 2, June 1991 (in the distribution as file licence.html,
0008 * and also available at http://gate.ac.uk/gate/licence.html).
0009 *
0010 * Thomas Heitz, 1 March 2010
0011 *
0012 * $Id$
0013 */
0014
0015 package gate.gui;
0016
0017 import gate.Factory;
0018 import gate.Resource;
0019 import gate.creole.AbstractVisualResource;
0020 import gate.creole.ResourceInstantiationException;
0021 import gate.creole.gazetteer.*;
0022 import gate.swing.XJFileChooser;
0023 import gate.swing.XJTable;
0024 import gate.util.Err;
0025 import gate.util.ExtensionFileFilter;
0026 import gate.util.Files;
0027 import gate.util.GateRuntimeException;
0028
0029 import javax.swing.*;
0030 import javax.swing.event.*;
0031 import javax.swing.table.AbstractTableModel;
0032 import javax.swing.table.DefaultTableCellRenderer;
0033 import javax.swing.table.DefaultTableModel;
0034 import javax.swing.text.BadLocationException;
0035 import javax.swing.text.Document;
0036 import javax.swing.text.JTextComponent;
0037 import java.awt.*;
0038 import java.awt.datatransfer.Clipboard;
0039 import java.awt.datatransfer.DataFlavor;
0040 import java.awt.datatransfer.UnsupportedFlavorException;
0041 import java.awt.event.*;
0042 import java.io.File;
0043 import java.io.FilenameFilter;
0044 import java.io.IOException;
0045 import java.net.MalformedURLException;
0046 import java.net.URL;
0047 import java.text.Collator;
0048 import java.util.*;
0049 import java.util.List;
0050 import java.util.Timer;
0051 import java.util.regex.Matcher;
0052 import java.util.regex.Pattern;
0053 import java.util.regex.PatternSyntaxException;
0054
0055 /**
0056 * Editor for {@link gate.creole.gazetteer.Gazetteer ANNIE Gazetteer}.
0057 <pre>
0058 Main features:
0059 - left table with 4 columns (List name, Major, Minor, Language) for the
0060 definition
0061 - right table with 1+n columns (Value, Feature 1...Feature n) for the lists
0062 - 'Save' on the context menu of the resources tree and tab
0063 - context menu on both tables to delete selected rows
0064 - list name drop down list with .lst files in directory and button [New List]
0065 - value text field and button [New Entry]
0066 - for the second table: [Add Cols]
0067 - a text field case insensitive 'Filter' at the bottom of the right table
0068 - both tables sorted case insensitively on the first column by default
0069 - display in red the list name when the list is modified
0070 - for the separator character test when editing feature columns
0071 - make feature map ordered
0072 - remove feature/value columns when containing only spaces or empty
0073 </pre>
0074 */
0075 public class GazetteerEditor extends AbstractVisualResource
0076 implements GazetteerListener, ActionsPublisher {
0077
0078 public GazetteerEditor() {
0079 definitionTableModel = new DefaultTableModel();
0080 definitionTableModel.addColumn("List name");
0081 definitionTableModel.addColumn("Major");
0082 definitionTableModel.addColumn("Minor");
0083 definitionTableModel.addColumn("Language");
0084 listTableModel = new ListTableModel();
0085 actions = new ArrayList<Action>();
0086 actions.add(new SaveAndReinitialiseGazetteerAction());
0087 actions.add(new SaveAsGazetteerAction());
0088 }
0089
0090 public Resource init() throws ResourceInstantiationException {
0091 initGUI();
0092 initListeners();
0093 return this;
0094 }
0095
0096 protected void initGUI() {
0097 collator = Collator.getInstance(Locale.ENGLISH);
0098 collator.setStrength(Collator.TERTIARY);
0099
0100 // definition table pane
0101 JPanel definitionPanel = new JPanel(new BorderLayout());
0102 JPanel definitionTopPanel = new JPanel();
0103 newListComboBox = new JComboBox();
0104 newListComboBox.setEditable(true);
0105 newListComboBox.setPrototypeDisplayValue("123456789012345");
0106 newListComboBox.setToolTipText(
0107 "Lists available in the gazetteer directory");
0108 newListButton = new JButton("New List");
0109 // enable/disable [New] button according to the text field content
0110 JTextComponent listTextComponent = (JTextField)
0111 newListComboBox.getEditor().getEditorComponent();
0112 listTextComponent.getDocument().addDocumentListener(new DocumentListener() {
0113 public void insertUpdate(DocumentEvent e) { update(e); }
0114 public void removeUpdate(DocumentEvent e) { update(e); }
0115 public void changedUpdate(DocumentEvent e) { update(e); }
0116 public void update(DocumentEvent e) {
0117 Document document = e.getDocument();
0118 try {
0119 String value = document.getText(0, document.getLength());
0120 if (value.trim().length() == 0) {
0121 newListButton.setEnabled(false);
0122 newListButton.setText("New List");
0123 } else if (value.contains(":")) {
0124 newListButton.setEnabled(false);
0125 newListButton.setText("No colon");
0126 } else if (linearDefinition.getLists().contains(value)) {
0127 // this list already exists in the gazetteer
0128 newListButton.setEnabled(false);
0129 newListButton.setText("Existing");
0130 } else {
0131 newListButton.setEnabled(true);
0132 newListButton.setText("New List");
0133 }
0134 } catch (BadLocationException ble) {
0135 ble.printStackTrace();
0136 }
0137 }
0138 });
0139 newListComboBox.getEditor().getEditorComponent()
0140 .addKeyListener(new KeyAdapter() {
0141 public void keyPressed(KeyEvent e) {
0142 if (e.getKeyCode() == KeyEvent.VK_ENTER) {
0143 // Enter key in the text field add the entry to the table
0144 newListButton.doClick();
0145 }
0146 }
0147 });
0148 newListButton.setToolTipText("New list in the gazetteer");
0149 newListButton.setMargin(new Insets(2, 2, 2, 2));
0150 newListButton.addActionListener(new AbstractAction() {
0151 public void actionPerformed(ActionEvent e) {
0152 String listName = (String) newListComboBox.getEditor().getItem();
0153 newListComboBox.removeItem(listName);
0154 // update the table
0155 definitionTableModel.addRow(new Object[]{listName, "", "", ""});
0156 // update the gazetteer
0157 linearDefinition.add(new LinearNode(listName, "", "", ""));
0158 final int row = definitionTable.rowModelToView(
0159 definitionTable.getRowCount()-1);
0160 final int column = definitionTable.convertColumnIndexToView(0);
0161 definitionTable.setRowSelectionInterval(row, row);
0162 SwingUtilities.invokeLater(new Runnable() {
0163 public void run() {
0164 definitionTable.scrollRectToVisible(
0165 definitionTable.getCellRect(row, column, true));
0166 definitionTable.requestFocusInWindow();
0167 }
0168 });
0169 }
0170 });
0171 definitionTopPanel.add(newListComboBox);
0172 definitionTopPanel.add(newListButton);
0173 definitionPanel.add(definitionTopPanel, BorderLayout.NORTH);
0174 definitionTable = new XJTable() {
0175 // shift + Delete keys delete the selected rows
0176 protected void processKeyEvent(KeyEvent e) {
0177 if (e.getKeyCode() == KeyEvent.VK_DELETE
0178 && ((e.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) != 0)) {
0179 new DeleteSelectedLinearNodeAction().actionPerformed(null);
0180 } else {
0181 super.processKeyEvent(e);
0182 }
0183 }
0184 };
0185 definitionTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
0186 definitionTable.setRowSelectionAllowed(true);
0187 definitionTable.setColumnSelectionAllowed(false);
0188 definitionTable.setEnableHidingColumns(true);
0189 definitionTable.setAutoResizeMode(XJTable.AUTO_RESIZE_OFF);
0190 definitionTable.setModel(definitionTableModel);
0191 definitionTable.setSortable(true);
0192 definitionTable.setSortedColumn(0);
0193 // use red colored font for modified lists name
0194 definitionTable.getColumnModel().getColumn(0).setCellRenderer(
0195 new DefaultTableCellRenderer() {
0196 public Component getTableCellRendererComponent(JTable table,
0197 Object value, boolean isSelected, boolean hasFocus,
0198 int row, int column) {
0199 super.getTableCellRendererComponent(
0200 table, value, isSelected, hasFocus, row, column);
0201 setForeground(table.getForeground());
0202 LinearNode linearNode = (LinearNode)
0203 linearDefinition.getNodesByListNames().get(value);
0204 if (linearNode != null) {
0205 GazetteerList gazetteerList = (GazetteerList)
0206 linearDefinition.getListsByNode().get(linearNode);
0207 if (gazetteerList != null && gazetteerList.isModified()) {
0208 setForeground(Color.RED);
0209 }
0210 }
0211 return this;
0212 }
0213 });
0214 definitionPanel.add(new JScrollPane(definitionTable), BorderLayout.CENTER);
0215
0216 // list table pane
0217 JPanel listPanel = new JPanel(new BorderLayout());
0218 JPanel listTopPanel = new JPanel();
0219 newEntryTextField = new JTextField(10);
0220 newEntryTextField.setEnabled(false);
0221 final JButton newEntryButton = new JButton("New Entry ");
0222 newEntryButton.setToolTipText("New entry in the list");
0223 newEntryButton.setMargin(new Insets(2, 2, 2, 2));
0224 newEntryButton.setEnabled(false);
0225 newEntryButton.addActionListener(new AbstractAction() {
0226 public void actionPerformed(ActionEvent e) {
0227 // update the gazetteer
0228 GazetteerNode newGazetteerNode = new GazetteerNode(
0229 newEntryTextField.getText(), Factory.newFeatureMap());
0230 listTableModel.addRow(newGazetteerNode);
0231 listTableModel.setFilterText("");
0232 listFilterTextField.setText("");
0233 listTableModel.fireTableDataChanged();
0234 // scroll and select the new row
0235 final int row = listTable.rowModelToView(listTable.getRowCount()-1);
0236 final int column = listTable.convertColumnIndexToView(0);
0237 newEntryTextField.setText("");
0238 newEntryTextField.requestFocusInWindow();
0239 SwingUtilities.invokeLater(new Runnable() {
0240 public void run() {
0241 listTable.scrollRectToVisible(
0242 listTable.getCellRect(row, column, true));
0243 listTable.setRowSelectionInterval(row, row);
0244 listTable.setColumnSelectionInterval(column, column);
0245 GazetteerList gazetteerList = (GazetteerList)
0246 linearDefinition.getListsByNode().get(selectedLinearNode);
0247 gazetteerList.setModified(true);
0248 definitionTable.repaint();
0249 }
0250 });
0251 }
0252 });
0253 // Enter key in the text field add the entry to the table
0254 newEntryTextField.addKeyListener(new KeyAdapter() {
0255 public void keyPressed(KeyEvent e) {
0256 if (e.getKeyCode() == KeyEvent.VK_ENTER) {
0257 newEntryButton.doClick();
0258 }
0259 }
0260 });
0261 // enable/disable [New] button according to the text field content
0262 newEntryTextField.getDocument().addDocumentListener(new DocumentListener() {
0263 public void insertUpdate(DocumentEvent e) { update(e); }
0264 public void removeUpdate(DocumentEvent e) { update(e); }
0265 public void changedUpdate(DocumentEvent e) { update(e); }
0266 public void update(DocumentEvent e) {
0267 Document document = e.getDocument();
0268 try {
0269 String value = document.getText(0, document.getLength());
0270 if (value.trim().length() == 0) {
0271 newEntryButton.setEnabled(false);
0272 newEntryButton.setText("New Entry");
0273 } else if (linearDefinition.getSeparator() != null
0274 && linearDefinition.getSeparator().length() > 0
0275 && value.contains(linearDefinition.getSeparator())) {
0276 newEntryButton.setEnabled(false);
0277 newEntryButton.setText("No char "+linearDefinition.getSeparator());
0278 } else {
0279 // check if the entry already exists in the list
0280 GazetteerList gazetteerList = (GazetteerList)
0281 linearDefinition.getListsByNode().get(selectedLinearNode);
0282 boolean found = false;
0283 for (Object object : gazetteerList) {
0284 GazetteerNode node = (GazetteerNode) object;
0285 if (node.getEntry().equals(value)) {
0286 found = true;
0287 break;
0288 }
0289 }
0290 if (found) {
0291 newEntryButton.setEnabled(false);
0292 newEntryButton.setText("Existing ");
0293 } else {
0294 newEntryButton.setEnabled(true);
0295 newEntryButton.setText("New Entry");
0296 }
0297 }
0298 } catch (BadLocationException ble) {
0299 ble.printStackTrace();
0300 }
0301 }
0302 });
0303 addColumnsButton = new JButton("Add Cols");
0304 addColumnsButton.setToolTipText("Add a couple of columns Feature and Value");
0305 addColumnsButton.setMargin(new Insets(2, 2, 2, 2));
0306 addColumnsButton.setEnabled(false);
0307 addColumnsButton.addActionListener(new AbstractAction() {
0308 public void actionPerformed(ActionEvent e) {
0309 if (linearDefinition.getSeparator() == null
0310 || linearDefinition.getSeparator().length() == 0) {
0311 String separator = JOptionPane.showInputDialog(
0312 MainFrame.getInstance(), "Type a character separator to separate" +
0313 "\nfeatures in the gazetteers lists.",
0314 "Feature Separator", JOptionPane.QUESTION_MESSAGE);
0315 if (separator == null
0316 || separator.equals("")) {
0317 return;
0318 }
0319 linearDefinition.setSeparator(separator);
0320 }
0321 listTableModel.addEmptyFeatureColumns();
0322 // cancel filtering and redisplay the table
0323 listFilterTextField.setText("");
0324 listTableModel.setFilterText("");
0325 listTableModel.fireTableStructureChanged();
0326 }
0327 });
0328 listTopPanel.add(newEntryTextField);
0329 listTopPanel.add(newEntryButton);
0330 listTopPanel.add(addColumnsButton);
0331 listPanel.add(listTopPanel, BorderLayout.NORTH);
0332 listTable = new XJTable() {
0333 // shift + Delete keys delete the selected rows
0334 protected void processKeyEvent(KeyEvent e) {
0335 if (e.getKeyCode() == KeyEvent.VK_DELETE
0336 && ((e.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) != 0)) {
0337 new DeleteSelectedGazetteerNodeAction().actionPerformed(null);
0338 } else {
0339 super.processKeyEvent(e);
0340 }
0341 }
0342 };
0343 listTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
0344 listTable.setRowSelectionAllowed(true);
0345 listTable.setColumnSelectionAllowed(true);
0346 listTable.setEnableHidingColumns(true);
0347 listTable.setAutoResizeMode(XJTable.AUTO_RESIZE_OFF);
0348 listTable.setModel(listTableModel);
0349 listTable.setSortable(true);
0350 listTable.setSortedColumn(0);
0351 listPanel.add(new JScrollPane(listTable), BorderLayout.CENTER);
0352 JPanel listBottomPanel = new JPanel(new BorderLayout());
0353 JPanel filterPanel = new JPanel();
0354 listFilterTextField = new JTextField(15);
0355 listFilterTextField.setToolTipText("Filter rows on all column values");
0356 listFilterTextField.setEnabled(false);
0357 // select all the rows containing the text from filterTextField
0358 listFilterTextField.getDocument().addDocumentListener(
0359 new DocumentListener() {
0360 private Timer timer = new Timer("Gazetteer list filter timer", true);
0361 private TimerTask timerTask;
0362 public void changedUpdate(DocumentEvent e) { /* do nothing */ }
0363 public void insertUpdate(DocumentEvent e) { update(); }
0364 public void removeUpdate(DocumentEvent e) { update(); }
0365 private void update() {
0366 if (timerTask != null) { timerTask.cancel(); }
0367 Date timeToRun = new Date(System.currentTimeMillis() + 300);
0368 timerTask = new TimerTask() { public void run() {
0369 String filter = listFilterTextField.getText().trim();
0370 listTableModel.setFilterText(filter);
0371 listTableModel.fireTableDataChanged();
0372 }};
0373 // add a delay
0374 timer.schedule(timerTask, timeToRun);
0375 }
0376 });
0377 filterPanel.add(new JLabel("Filter: "));
0378 filterPanel.add(listFilterTextField);
0379 filterPanel.add(caseInsensitiveCheckBox = new JCheckBox("Case Ins."));
0380 caseInsensitiveCheckBox.setSelected(true);
0381 caseInsensitiveCheckBox.setToolTipText("Case Insensitive");
0382 caseInsensitiveCheckBox.addActionListener(new ActionListener() {
0383 public void actionPerformed(ActionEvent e) {
0384 // redisplay the table with the new filter option
0385 listFilterTextField.setText(listFilterTextField.getText());
0386 }
0387 });
0388 filterPanel.add(regexCheckBox = new JCheckBox("Regex"));
0389 regexCheckBox.setToolTipText("Regular Expression");
0390 regexCheckBox.addActionListener(new ActionListener() {
0391 public void actionPerformed(ActionEvent e) {
0392 listFilterTextField.setText(listFilterTextField.getText());
0393 }
0394 });
0395 filterPanel.add(onlyValueCheckBox = new JCheckBox("Value"));
0396 onlyValueCheckBox.setToolTipText("Filter only Value column");
0397 onlyValueCheckBox.addActionListener(new ActionListener() {
0398 public void actionPerformed(ActionEvent e) {
0399 listFilterTextField.setText(listFilterTextField.getText());
0400 }
0401 });
0402 listBottomPanel.add(filterPanel, BorderLayout.WEST);
0403 listBottomPanel.add(listCountLabel = new JLabel(), BorderLayout.EAST);
0404 listPanel.add(listBottomPanel, BorderLayout.SOUTH);
0405
0406 JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
0407 splitPane.add(definitionPanel);
0408 splitPane.add(listPanel);
0409 splitPane.setResizeWeight(0.33);
0410 setLayout(new BorderLayout());
0411 add(splitPane, BorderLayout.CENTER);
0412 }
0413
0414 protected void initListeners() {
0415
0416 // display the list corresponding to the selected row
0417 definitionTable.getSelectionModel().addListSelectionListener(
0418 new ListSelectionListener() {
0419 public void valueChanged(ListSelectionEvent e) {
0420 if (e.getValueIsAdjusting()
0421 || definitionTable.isEditing()) {
0422 return;
0423 }
0424 if (definitionTable.getSelectedRow() == -1) { // no list selected
0425 listTableModel.setGazetteerList(new GazetteerList());
0426 selectedLinearNode = null;
0427 newEntryTextField.setEnabled(false);
0428 addColumnsButton.setEnabled(false);
0429 listFilterTextField.setEnabled(false);
0430 } else { // list selected
0431 String listName = (String) definitionTable.getValueAt(
0432 definitionTable.getSelectedRow(),
0433 definitionTable.convertColumnIndexToView(0));
0434 selectedLinearNode = (LinearNode)
0435 linearDefinition.getNodesByListNames().get(listName);
0436 if (selectedLinearNode != null) {
0437 listTableModel.setGazetteerList((GazetteerList)
0438 linearDefinition.getListsByNode().get(selectedLinearNode));
0439 }
0440 newEntryTextField.setEnabled(true);
0441 addColumnsButton.setEnabled(true);
0442 listFilterTextField.setEnabled(true);
0443 }
0444 if (!listFilterTextField.getText().equals("")) {
0445 listFilterTextField.setText("");
0446 }
0447 if (!newEntryTextField.getText().equals("")) {
0448 newEntryTextField.setText("");
0449 }
0450 listTableModel.setFilterText("");
0451 listTableModel.fireTableStructureChanged();
0452 if (definitionTable.getSelectedRow() != -1) {
0453 if (selectedLinearNode != null) {
0454 for (int col = 0 ; col < listTable.getColumnCount(); col++) {
0455 listTable.setComparator(col, collator);
0456 }
0457 // TODO: this is only to sort the rows, how to avoid it?
0458 listTableModel.fireTableDataChanged();
0459 }
0460 }
0461 }
0462 }
0463 );
0464
0465 // update linear nodes with changes in the definition table
0466 definitionTableModel.addTableModelListener(new TableModelListener() {
0467 public void tableChanged(TableModelEvent e) {
0468 int r = e.getFirstRow();
0469 switch (e.getType()) {
0470 case TableModelEvent.UPDATE:
0471 int c = e.getColumn();
0472 if (r == -1 || c == -1) { return; }
0473 String newValue = (String) definitionTableModel.getValueAt(r, c);
0474 if (c == 0) {
0475 String oldValue = selectedLinearNode.getList();
0476 if (oldValue != null && oldValue.equals(newValue)) { return; }
0477 // save the previous list and copy it to the new name of the list
0478 try {
0479 GazetteerList gazetteerList = (GazetteerList)
0480 linearDefinition.getListsByNode().get(selectedLinearNode);
0481 // save the previous list
0482 gazetteerList.store();
0483 MainFrame.getInstance().statusChanged("Previous list saved in "
0484 + gazetteerList.getURL().getPath());
0485 File source = Files.fileFromURL(gazetteerList.getURL());
0486 File destination = new File(source.getParentFile(), newValue);
0487 // change the list URL to the new list name
0488 gazetteerList.setURL(destination.toURI().toURL());
0489 gazetteerList.setModified(false);
0490 // change the key of the node in the map
0491 linearDefinition.getNodesByListNames()
0492 .remove(selectedLinearNode.getList());
0493 linearDefinition.getNodesByListNames()
0494 .put(newValue, selectedLinearNode);
0495 linearDefinition.setModified(true);
0496
0497 } catch (Exception ex) {
0498 MainFrame.getInstance().statusChanged(
0499 "Unable to save the list.");
0500 Err.prln("Unable to save the list.\n" + ex.getMessage());
0501 }
0502
0503 selectedLinearNode.setList(newValue);
0504 } else if (c == 1) {
0505 String oldValue = selectedLinearNode.getMajorType();
0506 if (oldValue != null && oldValue.equals(newValue)) { return; }
0507 selectedLinearNode.setMajorType(newValue);
0508 linearDefinition.setModified(true);
0509 } else if (c == 2) {
0510 String oldValue = selectedLinearNode.getMinorType();
0511 if (oldValue != null && oldValue.equals(newValue)) { return; }
0512 selectedLinearNode.setMinorType(newValue);
0513 linearDefinition.setModified(true);
0514 } else {
0515 String oldValue = selectedLinearNode.getLanguage();
0516 if (oldValue != null && oldValue.equals(newValue)) { return; }
0517 selectedLinearNode.setLanguage(newValue);
0518 linearDefinition.setModified(true);
0519 }
0520 break;
0521 }
0522 }
0523 });
0524
0525 // context menu to delete a row
0526 definitionTable.addMouseListener(new MouseAdapter() {
0527 public void mouseClicked(MouseEvent me) {
0528 processMouseEvent(me);
0529 }
0530 public void mouseReleased(MouseEvent me) {
0531 processMouseEvent(me);
0532 }
0533 public void mousePressed(MouseEvent me) {
0534 JTable table = (JTable) me.getSource();
0535 int row = table.rowAtPoint(me.getPoint());
0536 if(me.isPopupTrigger()
0537 && !table.isRowSelected(row)) {
0538 // if right click outside the selection then reset selection
0539 table.getSelectionModel().setSelectionInterval(row, row);
0540 }
0541 processMouseEvent(me);
0542 }
0543 protected void processMouseEvent(MouseEvent me) {
0544 XJTable table = (XJTable) me.getSource();
0545 if (me.isPopupTrigger()
0546 && table.getSelectedRowCount() > 0) {
0547 JPopupMenu popup = new JPopupMenu();
0548 popup.add(new ReloadGazetteerListAction());
0549 popup.addSeparator();
0550 popup.add(new DeleteSelectedLinearNodeAction());
0551 popup.show(table, me.getX(), me.getY());
0552 }
0553 }
0554 });
0555
0556 // context menu to delete a row
0557 listTable.addMouseListener(new MouseAdapter() {
0558 public void mouseClicked(MouseEvent me) {
0559 processMouseEvent(me);
0560 }
0561 public void mouseReleased(MouseEvent me) {
0562 processMouseEvent(me);
0563 }
0564 public void mousePressed(MouseEvent me) {
0565 JTable table = (JTable) me.getSource();
0566 int row = table.rowAtPoint(me.getPoint());
0567 if(me.isPopupTrigger()
0568 && !table.isRowSelected(row)) {
0569 // if right click outside the selection then reset selection
0570 table.getSelectionModel().setSelectionInterval(row, row);
0571 }
0572 processMouseEvent(me);
0573 }
0574 protected void processMouseEvent(MouseEvent me) {
0575 XJTable table = (XJTable) me.getSource();
0576 if (me.isPopupTrigger()
0577 && table.getSelectedRowCount() > 0) {
0578 JPopupMenu popup = new JPopupMenu();
0579 popup.add(new CopySelectionAction());
0580 popup.add(new PasteSelectionAction());
0581 popup.addSeparator();
0582 popup.add(new FillDownSelectionAction());
0583 popup.add(new ClearSelectionAction());
0584 popup.addSeparator();
0585 popup.add(new DeleteSelectedGazetteerNodeAction());
0586 popup.show(table, me.getX(), me.getY());
0587 }
0588 }
0589 });
0590
0591 listTableModel.addTableModelListener(new TableModelListener() {
0592 public void tableChanged(TableModelEvent e) {
0593 listCountLabel.setText(String.valueOf(listTableModel.getRowCount())
0594 + " entries ");
0595 }
0596 });
0597
0598 // add key shortcuts for global actions
0599 InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
0600 ActionMap actionMap = getActionMap();
0601 inputMap.put(KeyStroke.getKeyStroke("control S"), "save");
0602 actionMap.put("save", actions.get(0));
0603 inputMap.put(KeyStroke.getKeyStroke("control shift S"), "save as");
0604 actionMap.put("save as", actions.get(1));
0605 inputMap.put(KeyStroke.getKeyStroke("control R"), "reload list");
0606 actionMap.put("reload list", new ReloadGazetteerListAction());
0607
0608 // add key shortcuts for the list table actions
0609 inputMap = listTable.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
0610 actionMap = listTable.getActionMap();
0611 inputMap.put(KeyStroke.getKeyStroke("control V"), "paste table selection");
0612 actionMap.put("paste table selection", new PasteSelectionAction());
0613 }
0614
0615 public void setTarget(Object target) {
0616 if (null == target) {
0617 throw new GateRuntimeException("The resource set is null.");
0618 }
0619 if (! (target instanceof Gazetteer) ) {
0620 throw new GateRuntimeException(
0621 "The resource set must be of type gate.creole.gazetteer.Gazetteer\n"+
0622 "and not " + target.getClass());
0623 }
0624 ((Gazetteer) target).addGazetteerListener(this);
0625 processGazetteerEvent(new GazetteerEvent(target, GazetteerEvent.REINIT));
0626 }
0627
0628 public void processGazetteerEvent(GazetteerEvent e) {
0629 gazetteer = (Gazetteer) e.getSource();
0630
0631 // read and display the definition of the gazetteer
0632 if (e.getType() == GazetteerEvent.REINIT) {
0633 linearDefinition = gazetteer.getLinearDefinition();
0634 if (null == linearDefinition) {
0635 throw new GateRuntimeException(
0636 "Linear definition of a gazetteer should not be null.");
0637 }
0638
0639 // reload the lists with ordered feature maps
0640 try {
0641 if (linearDefinition.getSeparator() != null
0642 && linearDefinition.getSeparator().length() > 0) {
0643 linearDefinition.loadLists(true);
0644 }
0645 } catch (ResourceInstantiationException rie) {
0646 rie.printStackTrace();
0647 return;
0648 }
0649
0650 // add the gazetteer definition data to the table
0651 definitionTableModel.setRowCount(0);
0652 ArrayList<String> values = new ArrayList<String>();
0653 for (Object object : linearDefinition.getNodes()) {
0654 LinearNode node = (LinearNode) object;
0655 values.add(node.getList() == null ? "" : node.getList());
0656 values.add(node.getMajorType() == null ? "" : node.getMajorType());
0657 values.add(node.getMinorType() == null ? "" : node.getMinorType());
0658 values.add(node.getLanguage() == null ? "" : node.getLanguage());
0659 definitionTableModel.addRow(values.toArray());
0660 values.clear();
0661 }
0662 for (int col = 0 ; col < definitionTable.getColumnCount(); col++) {
0663 definitionTable.setComparator(col, collator);
0664 }
0665
0666 // update file list name in the drop down list
0667 File gazetteerDirectory = new File(
0668 Files.fileFromURL(gazetteer.getListsURL()).getParent());
0669 File[] files = gazetteerDirectory.listFiles(new FilenameFilter() {
0670 public boolean accept(File dir, String name) {
0671 return name.endsWith(".lst")
0672 && !linearDefinition.getLists().contains(name);
0673 }
0674 });
0675 String[] filenames = new String[files.length];
0676 int i = 0;
0677 for (File file : files) {
0678 filenames[i++] = file.getName();
0679 }
0680 Arrays.sort(filenames, collator);
0681 newListComboBox.setModel(new DefaultComboBoxModel(filenames));
0682 if (filenames.length == 0) {
0683 newListButton.setEnabled(false);
0684 }
0685 }
0686 }
0687
0688 protected class ListTableModel extends AbstractTableModel {
0689
0690 public ListTableModel() {
0691 gazetteerListFiltered = new GazetteerList();
0692 }
0693
0694 public int getRowCount() {
0695 return gazetteerListFiltered.size();
0696 }
0697
0698 public int getColumnCount() {
0699 if (columnCount > -1) { return columnCount; }
0700 if (gazetteerListFiltered == null) { return 0; }
0701 columnCount = 1;
0702 // read all the features maps to find the biggest one
0703 for (Object object : gazetteerListFiltered) {
0704 GazetteerNode node = (GazetteerNode) object;
0705 Map map = node.getFeatureMap();
0706 if (map != null && columnCount < 2*map.size()+1) {
0707 columnCount = 2*map.size() + 1;
0708 }
0709 }
0710 return columnCount;
0711 }
0712
0713 public String getColumnName(int column) {
0714 if (column == 0) {
0715 return "Value";
0716 } else {
0717 int featureCount = (column + (column % 2)) / 2;
0718 if (column % 2 == 1) {
0719 return "Feature " + featureCount;
0720 } else {
0721 return "Value " + featureCount;
0722 }
0723 }
0724 }
0725
0726 public boolean isCellEditable(int row, int column) {
0727 return true;
0728 }
0729
0730 public Object getValueAt(int row, int column) {
0731 GazetteerNode node = (GazetteerNode) gazetteerListFiltered.get(row);
0732 if (column == 0) {
0733 return node.getEntry();
0734 } else {
0735 Map featureMap = node.getFeatureMap();
0736 if (featureMap == null
0737 || featureMap.size()*2 < column) {
0738 return "";
0739 }
0740 List<String> features = new ArrayList<String>(featureMap.keySet());
0741 int featureCount = (column + (column % 2)) / 2;
0742 if (column % 2 == 1) {
0743 return features.get(featureCount-1);
0744 } else {
0745 return featureMap.get(features.get(featureCount-1));
0746 }
0747 }
0748 }
0749
0750 public void setValueAt(Object value, int row, int column) {
0751 if (row == -1 || column == -1) { return; }
0752 // remove separator characters that are contained in the value
0753 // and display a tooltip to explain it
0754 if (linearDefinition.getSeparator() != null
0755 && linearDefinition.getSeparator().length() > 0
0756 && ((String)value).contains(linearDefinition.getSeparator())) {
0757 final Point point = listTable.getCellRect(listTable.getSelectedRow(),
0758 listTable.getSelectedColumn(), true).getLocation();
0759 point.translate(listTable.getLocationOnScreen().x,
0760 listTable.getLocationOnScreen().y);
0761 final Timer timer = new Timer("GazetteerEditor tooltip timer", true);
0762 SwingUtilities.invokeLater(new Runnable() { public void run() {
0763 if (!listTable.isShowing()) { return; }
0764 JToolTip toolTip = listTable.createToolTip();
0765 toolTip.setTipText("No separator character allowed: [" +
0766 linearDefinition.getSeparator() + "]");
0767 PopupFactory popupFactory = PopupFactory.getSharedInstance();
0768 final Popup popup = popupFactory.getPopup(
0769 listTable, toolTip, point.x, point.y - 20);
0770 popup.show();
0771 Date timeToRun = new Date(System.currentTimeMillis() + 3000);
0772 timer.schedule(new TimerTask() { public void run() {
0773 SwingUtilities.invokeLater(new Runnable() { public void run() {
0774 popup.hide(); // hide the tooltip after some time
0775 }});
0776 }}, timeToRun);
0777 }});
0778 value = ((String)value).replaceAll(
0779 "\\Q"+linearDefinition.getSeparator()+"\\E", "");
0780 }
0781 GazetteerNode gazetteerNode =
0782 (GazetteerNode) gazetteerListFiltered.get(row);
0783 if (column == 0) {
0784 // update entry
0785 gazetteerNode.setEntry((String) value);
0786 } else {
0787 // update the whole feature map
0788 Map newFeatureMap = new LinkedHashMap();
0789 for (int col = 1; col+1 < getColumnCount(); col += 2) {
0790 String feature = (String) ((col == column) ?
0791 value : getValueAt(row, col));
0792 String val = (String) ((col+1 == column) ?
0793 value : (String) getValueAt(row, col+1));
0794 newFeatureMap.put(feature, val);
0795 }
0796 gazetteerNode.setFeatureMap(newFeatureMap);
0797 fireTableRowsUpdated(row, row);
0798 }
0799 gazetteerList.setModified(true);
0800 definitionTable.repaint();
0801 }
0802
0803 public void fireTableStructureChanged() {
0804 columnCount = -1;
0805 super.fireTableStructureChanged();
0806 }
0807
0808 public void fireTableChanged(TableModelEvent e) {
0809 if (gazetteerList == null) { return; }
0810 if (filter.length() < 2) {
0811 gazetteerListFiltered.clear();
0812 gazetteerListFiltered.addAll(gazetteerList);
0813 super.fireTableChanged(e);
0814 } else {
0815 filterRows();
0816 // same as super.fireTableDataChanged() to avoid recursion
0817 super.fireTableChanged(new TableModelEvent(this));
0818 }
0819 }
0820
0821 /**
0822 * Filter the table rows against this filter.
0823 * @param filter string used to filter rows
0824 */
0825 public void setFilterText(String filter) {
0826 this.filter = filter;
0827 }
0828
0829 protected void filterRows() {
0830 String patternText = filter;
0831 String prefixPattern = regexCheckBox.isSelected() ? "":"\\Q";
0832 String suffixPattern = regexCheckBox.isSelected() ? "":"\\E";
0833 patternText = prefixPattern + patternText + suffixPattern;
0834 Pattern pattern;
0835 try {
0836 pattern = caseInsensitiveCheckBox.isSelected() ?
0837 Pattern.compile(patternText, Pattern.CASE_INSENSITIVE) :
0838 Pattern.compile(patternText);
0839 } catch (PatternSyntaxException e) {
0840 return;
0841 }
0842 gazetteerListFiltered.clear();
0843 for (Object object : gazetteerList) {
0844 GazetteerNode node = (GazetteerNode) object;
0845 boolean match = false;
0846 Map map = node.getFeatureMap();
0847 if (map != null && !onlyValueCheckBox.isSelected()) {
0848 for (Object key : map.keySet()) {
0849 if (pattern.matcher((String) key).find()
0850 || pattern.matcher((String) map.get(key)).find()) {
0851 match = true;
0852 break;
0853 }
0854 }
0855 }
0856 if (match || pattern.matcher(node.getEntry()).find()) {
0857 // gazetteer node matches the filter
0858 gazetteerListFiltered.add(node);
0859 }
0860 }
0861 }
0862
0863 public void addEmptyFeatureColumns() {
0864 // find the first row fully filled with value
0865 if (getColumnCount() == 1) {
0866 GazetteerNode node = (GazetteerNode) gazetteerListFiltered.get(0);
0867 Map<String, String> map = new HashMap<String, String>();
0868 // add a couple of rows
0869 map.put("", "");
0870 node.setFeatureMap(map);
0871 } else {
0872 for (Object object : gazetteerListFiltered) {
0873 GazetteerNode node = (GazetteerNode) object;
0874 Map map = node.getFeatureMap();
0875 if (map != null
0876 && (2*map.size()+1) == getColumnCount()) {
0877 map.put("", "");
0878 break;
0879 }
0880 }
0881 }
0882 for (Object object : gazetteerList) {
0883 GazetteerNode node = (GazetteerNode) object;
0884 node.setSeparator(linearDefinition.getSeparator());
0885 }
0886 }
0887
0888 public void addRow(GazetteerNode gazetteerNode) {
0889 gazetteerList.add(gazetteerNode);
0890 }
0891
0892 /**
0893 * @param row row index in the model
0894 */
0895 public void removeRow(int row) {
0896 gazetteerList.remove(gazetteerListFiltered.get(row));
0897 }
0898
0899 public void setGazetteerList(GazetteerList gazetteerList) {
0900 this.gazetteerList = gazetteerList;
0901 }
0902
0903 private int columnCount = -1;
0904 private String filter = "";
0905 private GazetteerList gazetteerList;
0906 private GazetteerList gazetteerListFiltered;
0907 }
0908
0909 public List getActions() {
0910 return actions;
0911 }
0912
0913 protected class ReloadGazetteerListAction extends AbstractAction {
0914 public ReloadGazetteerListAction() {
0915 super("Reload List");
0916 putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control R"));
0917 }
0918 public void actionPerformed(ActionEvent e) {
0919 GazetteerList gazetteerList = (GazetteerList)
0920 linearDefinition.getListsByNode().get(selectedLinearNode);
0921 gazetteerList.clear();
0922 try {
0923 gazetteerList.load(true);
0924 } catch (ResourceInstantiationException rie) {
0925 rie.printStackTrace();
0926 return;
0927 }
0928 // reselect the row to redisplay the list
0929 int row = definitionTable.getSelectedRow();
0930 definitionTable.clearSelection();
0931 definitionTable.getSelectionModel().setSelectionInterval(row, row);
0932 }
0933 }
0934
0935 protected class SaveAndReinitialiseGazetteerAction extends AbstractAction {
0936 public SaveAndReinitialiseGazetteerAction() {
0937 super("Save and Reinitialise");
0938 putValue(SHORT_DESCRIPTION,
0939 "Save the definition and all the lists then reinitialise");
0940 putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control S"));
0941 }
0942 public void actionPerformed(ActionEvent e) {
0943 try {
0944 if (linearDefinition.isModified()) {
0945 linearDefinition.store();
0946 }
0947 for (Object object : linearDefinition.getListsByNode().values()) {
0948 GazetteerList gazetteerList = (GazetteerList) object;
0949 if (gazetteerList.isModified()) {
0950 gazetteerList.store();
0951 }
0952 }
0953 gazetteer.reInit();
0954 MainFrame.getInstance().statusChanged("Gazetteer saved in " +
0955 linearDefinition.getURL().getPath());
0956 definitionTable.repaint();
0957
0958 } catch (ResourceInstantiationException re) {
0959 MainFrame.getInstance().statusChanged(
0960 "Unable to save the Gazetteer.");
0961 Err.prln("Unable to save the Gazetteer.\n" + re.getMessage());
0962 }
0963 }
0964 }
0965
0966 protected class SaveAsGazetteerAction extends AbstractAction {
0967 public SaveAsGazetteerAction() {
0968 super("Save as...");
0969 putValue(SHORT_DESCRIPTION, "Save the definition and all the lists");
0970 putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control shift S"));
0971 }
0972 public void actionPerformed(ActionEvent e) {
0973 XJFileChooser fileChooser = MainFrame.getFileChooser();
0974 ExtensionFileFilter filter =
0975 new ExtensionFileFilter("Gazetteer files", "def");
0976 fileChooser.addChoosableFileFilter(filter);
0977 fileChooser.setMultiSelectionEnabled(false);
0978 fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
0979 fileChooser.setDialogTitle("Select a file name...");
0980 fileChooser.setResource(GazetteerEditor.class.getName());
0981 int result = fileChooser.showSaveDialog(GazetteerEditor.this);
0982 if (result == JFileChooser.APPROVE_OPTION) {
0983 File selectedFile = fileChooser.getSelectedFile();
0984 if (selectedFile == null) { return; }
0985 try {
0986 URL previousURL = linearDefinition.getURL();
0987 linearDefinition.setURL(selectedFile.toURI().toURL());
0988 linearDefinition.store();
0989 linearDefinition.setURL(previousURL);
0990 for (Object object : linearDefinition.getListsByNode().values()) {
0991 GazetteerList gazetteerList = (GazetteerList) object;
0992 previousURL = gazetteerList.getURL();
0993 gazetteerList.setURL(new File(selectedFile.getParentFile(),
0994 Files.fileFromURL(gazetteerList.getURL()).getName())
0995 .toURI().toURL());
0996 gazetteerList.store();
0997 gazetteerList.setURL(previousURL);
0998 gazetteerList.setModified(false);
0999 }
1000 MainFrame.getInstance().statusChanged("Gazetteer saved in " +
1001 selectedFile.getAbsolutePath());
1002 definitionTable.repaint();
1003
1004 } catch (ResourceInstantiationException re) {
1005 MainFrame.getInstance().statusChanged(
1006 "Unable to save the Gazetteer.");
1007 Err.prln("Unable to save the Gazetteer.\n" + re.getMessage());
1008 } catch (MalformedURLException mue) {
1009 mue.printStackTrace();
1010 }
1011 }
1012 }
1013 }
1014
1015 protected class DeleteSelectedLinearNodeAction extends AbstractAction {
1016 public DeleteSelectedLinearNodeAction() {
1017 super(definitionTable.getSelectedRowCount() > 1 ?
1018 "Delete Rows" : "Delete Row");
1019 putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("shift DELETE"));
1020 }
1021
1022 public void actionPerformed(ActionEvent e) {
1023 int[] rowsToDelete = definitionTable.getSelectedRows();
1024 definitionTable.clearSelection();
1025 for (int i = 0; i < rowsToDelete.length; i++) {
1026 rowsToDelete[i] = definitionTable.rowViewToModel(rowsToDelete[i]);
1027 }
1028 Arrays.sort(rowsToDelete);
1029 for (int i = rowsToDelete.length-1; i >= 0; i--) {
1030 definitionTableModel.removeRow(rowsToDelete[i]);
1031 linearDefinition.remove(rowsToDelete[i]);
1032 }
1033 }
1034 }
1035
1036 protected class DeleteSelectedGazetteerNodeAction extends AbstractAction {
1037 public DeleteSelectedGazetteerNodeAction() {
1038 super(listTable.getSelectedRowCount() > 1 ?
1039 "Delete Rows" : "Delete Row");
1040 putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("shift DELETE"));
1041 }
1042
1043 public void actionPerformed(ActionEvent e) {
1044 int[] rowsToDelete = listTable.getSelectedRows();
1045 listTable.clearSelection();
1046 for (int i = 0; i < rowsToDelete.length; i++) {
1047 rowsToDelete[i] = listTable.rowViewToModel(rowsToDelete[i]);
1048 }
1049 Arrays.sort(rowsToDelete);
1050 for (int i = rowsToDelete.length-1; i >= 0; i--) {
1051 listTableModel.removeRow(rowsToDelete[i]);
1052 }
1053 listTableModel.fireTableDataChanged();
1054 }
1055 }
1056
1057 protected class FillDownSelectionAction extends AbstractAction {
1058 public FillDownSelectionAction() {
1059 super("Fill Down Selection");
1060 putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control D"));
1061 }
1062
1063 public void actionPerformed(ActionEvent e) {
1064 int[] rows = listTable.getSelectedRows();
1065 int[] columns = listTable.getSelectedColumns();
1066 listTable.clearSelection();
1067 for (int column : columns) {
1068 String firstCell = (String) listTable.getValueAt(rows[0], column);
1069 for (int row : rows) {
1070 listTableModel.setValueAt(firstCell,
1071 listTable.rowViewToModel(row),
1072 listTable.convertColumnIndexToModel(column));
1073 }
1074 listTableModel.fireTableDataChanged();
1075 }
1076 }
1077 }
1078
1079 protected class CopySelectionAction extends AbstractAction {
1080 public CopySelectionAction() {
1081 super("Copy Selection");
1082 putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control C"));
1083 }
1084
1085 public void actionPerformed(ActionEvent e) {
1086 listTable.getActionMap().get("copy").actionPerformed(
1087 new ActionEvent(listTable, ActionEvent.ACTION_PERFORMED, null));
1088 // // generate a Control + V keyboard event
1089 // listTable.dispatchEvent(
1090 // new KeyEvent(listTable, KeyEvent.KEY_PRESSED,
1091 // e.getWhen()+1, KeyEvent.CTRL_DOWN_MASK,
1092 // KeyEvent.VK_V, KeyEvent.CHAR_UNDEFINED));
1093 }
1094 }
1095
1096 protected class PasteSelectionAction extends AbstractAction {
1097 public PasteSelectionAction() {
1098 super("Paste Selection");
1099 putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control V"));
1100 }
1101
1102 public void actionPerformed(ActionEvent e) {
1103 int firstRow = listTable.getSelectedRow();
1104 int firstColumn = listTable.getSelectedColumn();
1105 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
1106 String valueCopied = null;
1107 try {
1108 if (clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
1109 valueCopied = (String) clipboard.getContents(null)
1110 .getTransferData(DataFlavor.stringFlavor);
1111 }
1112 } catch (UnsupportedFlavorException e1) {
1113 e1.printStackTrace();
1114 } catch (IOException e1) {
1115 e1.printStackTrace();
1116 }
1117 if (valueCopied == null) { return; }
1118 int rowToPaste = firstRow;
1119 for (String rowCopied : valueCopied.split("\n")) {
1120 int columnToPaste = firstColumn;
1121 for (String cellCopied : rowCopied.split("\t")) {
1122 listTableModel.setValueAt(cellCopied,
1123 listTable.rowViewToModel(rowToPaste),
1124 listTable.convertColumnIndexToModel(columnToPaste));
1125 if (columnToPaste + 1 > listTable.getColumnCount()) { break; }
1126 columnToPaste++;
1127 }
1128 if (rowToPaste + 1 > listTable.getRowCount()) { break; }
1129 rowToPaste++;
1130 }
1131 listTableModel.fireTableDataChanged();
1132 }
1133 }
1134
1135 protected class ClearSelectionAction extends AbstractAction {
1136 public ClearSelectionAction() {
1137 super("Clear Selection");
1138 putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control DELETE"));
1139 }
1140
1141 public void actionPerformed(ActionEvent e) {
1142 int[] rows = listTable.getSelectedRows();
1143 int[] columns = listTable.getSelectedColumns();
1144 listTable.clearSelection();
1145 for (int column : columns) {
1146 for (int row : rows) {
1147 listTableModel.setValueAt("",
1148 listTable.rowViewToModel(row),
1149 listTable.convertColumnIndexToModel(column));
1150 }
1151 listTableModel.fireTableDataChanged();
1152 }
1153 }
1154 }
1155
1156 // local variables
1157 protected Gazetteer gazetteer;
1158 /** the linear definition being displayed */
1159 protected LinearDefinition linearDefinition;
1160 /** the linear node currently selected */
1161 protected LinearNode selectedLinearNode;
1162 protected Collator collator;
1163 protected List<Action> actions;
1164
1165 // user interface components
1166 protected XJTable definitionTable;
1167 protected DefaultTableModel definitionTableModel;
1168 protected XJTable listTable;
1169 protected ListTableModel listTableModel;
1170 protected JComboBox newListComboBox;
1171 protected JButton newListButton;
1172 protected JTextField newEntryTextField;
1173 protected JButton addColumnsButton;
1174 protected JTextField listFilterTextField;
1175 protected JCheckBox regexCheckBox;
1176 protected JCheckBox caseInsensitiveCheckBox;
1177 protected JCheckBox onlyValueCheckBox;
1178 protected JLabel listCountLabel;
1179 }
|