001package jmri.jmrit.display; 002 003import java.awt.*; 004import java.awt.event.ActionEvent; 005import java.awt.event.ActionListener; 006import java.beans.PropertyChangeEvent; 007import java.beans.PropertyChangeListener; 008import java.util.*; 009import java.util.List; 010import java.util.regex.Matcher; 011import java.util.regex.Pattern; 012 013import javax.annotation.Nonnull; 014import javax.swing.*; 015import javax.swing.table.*; 016 017import jmri.*; 018import jmri.NamedBean.DisplayOptions; 019import jmri.jmrit.logixng.Module; 020import jmri.jmrit.logixng.*; 021import jmri.jmrit.logixng.NamedTable.NamedTablePropertyChangeEvent; 022import jmri.jmrit.logixng.actions.*; 023import jmri.jmrit.logixng.implementation.*; 024import jmri.jmrit.logixng.util.LogixNG_Thread; 025import jmri.util.swing.*; 026import jmri.util.table.ButtonEditor; 027import jmri.util.table.ButtonRenderer; 028 029/** 030 * An icon to display a NamedTable and let the user edit it. 031 * 032 * @author Pete Cressman Copyright (c) 2009 033 * @author Daniel Bergqvist Copyright (C) 2025 034 * @since 5.15.1 035 */ 036public final class LogixNGTableIcon extends PositionableJPanel { 037 038 private TableModel _tableModel = null; 039 040 // the associated NamedTable object 041 private final JTable _table; 042 private final JList<Object> _rowHeader; 043 private final JScrollPane _scrollPane; 044 045 private final java.awt.event.MouseListener _mouseListener = JmriMouseListener.adapt(this); 046 047 public LogixNGTableIcon(String tableName, Editor editor) { 048 super(editor); 049 setDisplayLevel(Editor.LABELS); 050 051 setLayout(new java.awt.BorderLayout()); 052 053 _tableModel = new TableModel(tableName); 054 055 _table = new JTable(_tableModel); 056 _table.setCellSelectionEnabled(true); 057 _table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 058 _table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF ); 059 _table.getTableHeader().setReorderingAllowed(false); 060 061 for (int col=0; col < _tableModel.getColumnCount(); col++) { 062 TableModel.HeaderType headerType = _tableModel._headers[col]; 063 if (headerType._cellEditor != null) { 064 _table.getColumnModel().getColumn(col).setCellEditor(headerType._cellEditor); 065 } 066 } 067 068 ButtonRenderer buttonRenderer = new ButtonRenderer(); 069 _table.setDefaultRenderer(JButton.class, buttonRenderer); 070 TableCellEditor editButEditor = new ButtonEditor(new JButton()); 071 _table.setDefaultEditor(JButton.class, editButEditor); 072 073 ListModel<Object> lm = new RowHeaderListModel(); 074 075 _rowHeader = new JList<>(lm); 076 _rowHeader.setFixedCellHeight( 077 _table.getRowHeight() 078 ); 079 _rowHeader.setCellRenderer(new RowHeaderRenderer(_table)); 080 081 _scrollPane = new JScrollPane(_table); 082 _scrollPane.setRowHeaderView(_rowHeader); 083 add(_scrollPane, BorderLayout.CENTER); 084 085 _table.addMouseListener(_mouseListener); 086 _rowHeader.addMouseListener(_mouseListener); 087 _scrollPane.addMouseListener(_mouseListener); 088 setPopupUtility(new PositionablePopupUtil(this, _table)); 089 } 090 091 @Override 092 public Positionable deepClone() { 093 LogixNGTableIcon pos = new LogixNGTableIcon( 094 _tableModel.getTable().getDisplayName(), _editor); 095 return finishClone(pos); 096 } 097 098 protected Positionable finishClone(LogixNGTableIcon pos) { 099 _tableModel.setTable(_tableModel.getTable()); 100 return super.finishClone(pos); 101 } 102 103 public TableModel getTableModel() { 104 return _tableModel; 105 } 106 107 public JTable getJTable() { 108 return _table; 109 } 110 111 @Override 112 @Nonnull 113 public String getTypeString() { 114 return Bundle.getMessage("PositionableType_LogixNGTableIcon"); 115 } 116 117 @Override 118 public String getNameString() { 119 String name; 120 if (_tableModel.getTable() == null) { 121 name = Bundle.getMessage("NotConnected"); 122 } else { 123 name = _tableModel.getTable().getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME); 124 } 125 return name; 126 } 127 128 private void configureLogixNGTable() { 129 NamedTable namedTable = _tableModel.getTable(); 130 131 JComboBox<ModuleItem> _validateModuleComboBox; 132 _validateModuleComboBox = new JComboBox<>(); 133 _validateModuleComboBox.addItem(new ModuleItem(null)); 134 for (Module m : InstanceManager.getDefault(ModuleManager.class).getNamedBeanSet()) { 135 if ("DefaultFemaleDigitalActionSocket".equals(m.getRootSocketType().getName()) 136 && m.isVisible()) { 137 ModuleItem mi = new ModuleItem(m); 138 _validateModuleComboBox.addItem(mi); 139 Module _validateModule = _tableModel.getValidateModule(); 140 if (_validateModule == m) { 141 _validateModuleComboBox.setSelectedItem(mi); 142 } 143 } 144 } 145 JComboBoxUtil.setupComboBoxMaxRows(_validateModuleComboBox); 146 147 Map<String,Integer> columnIndexes = new HashMap<>(); 148 List<String> columns = new ArrayList<>(); 149 for (int col=0; col < namedTable.numColumns(); col++) { 150 String header = getObjectAsString(namedTable.getCell(0, col+1)); 151 if (!header.isEmpty()) { 152 columns.add(header); 153 columnIndexes.put(header, col); 154 } 155 } 156 JList<String> columnList = new JList<>(columns.toArray(new String[0])); 157 for (String header : _tableModel._editableColumnsList) { 158 int index = columnIndexes.getOrDefault(header,-1); 159 if (index != -1) { 160 columnList.getSelectionModel().addSelectionInterval(index, index); 161 } 162 } 163 columnList.setBorder(BorderFactory.createLineBorder(Color.black)); 164 165 JDialog dialog = new JDialog(); 166 dialog.setTitle(Bundle.getMessage("ConfigureLogixNGTable")); 167 dialog.setLocationRelativeTo(this); 168 dialog.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); 169 170 JPanel p; 171 p = new JPanel(); 172 p.setLayout(new java.awt.GridBagLayout()); 173 java.awt.GridBagConstraints c = new java.awt.GridBagConstraints(); 174 c.gridwidth = 1; 175 c.gridheight = 1; 176 c.gridx = 0; 177 c.gridy = 0; 178 c.anchor = java.awt.GridBagConstraints.EAST; 179 //c.gridx = 0; 180 c.gridy = 1; 181 JLabel validateLabel = new JLabel(Bundle.getMessage("LogixNGTableIcon_ValidateLogixNGModule")); 182 p.add(validateLabel, c); 183 validateLabel.setLabelFor(_validateModuleComboBox); 184 c.gridy = 2; 185 JLabel allowEditLabel = new JLabel(Bundle.getMessage("LogixNGTableIcon_AllowEditTable")); 186 JCheckBox allowEdit = new JCheckBox(); 187 allowEdit.setSelected(_tableModel.isEditable()); 188 p.add(allowEditLabel, c); 189 allowEditLabel.setLabelFor(allowEdit); 190 c.gridy = 3; 191 p.add(new JLabel(Bundle.getMessage("LogixNGTableIcon_EditableColumns")), c); 192 c.gridx = 1; 193 c.gridy = 0; 194 p.add(Box.createHorizontalStrut(5), c); 195 c.gridx = 2; 196 c.gridy = 2; 197 c.anchor = java.awt.GridBagConstraints.WEST; 198 c.weightx = 1.0; 199 c.fill = java.awt.GridBagConstraints.HORIZONTAL; // text field will expand 200 p.add(allowEdit, c); 201 c.gridx = 2; 202 c.gridy = 1; 203 p.add(_validateModuleComboBox, c); 204// sys.setToolTipText(Bundle.getMessage("SysNameToolTip", "Y")); 205 c.gridy = 3; 206 p.add(new JScrollPane(columnList), c); 207 c.gridy = 4; 208 p.add(Box.createVerticalStrut(5), c); 209 c.gridx = 0; 210 c.gridy = 5; 211 c.gridwidth = 3; 212 p.add(new JLabel(Bundle.getMessage("LogixNGTableIcon_EditableColumnsInfo")), c); 213 add(p); 214 215 // cancel + add buttons at bottom of window 216 JPanel panelBottom = new JPanel(); 217 panelBottom.setLayout(new FlowLayout(FlowLayout.TRAILING)); 218 219 JButton cancel; 220 panelBottom.add(cancel = new JButton(Bundle.getMessage("ButtonCancel"))); 221 cancel.addActionListener((evt) -> { 222 dialog.dispose(); 223 }); 224 225 JButton ok; 226// panelBottom.add(ok = new JButton(Bundle.getMessage(addButtonLabel))); 227 panelBottom.add(ok = new JButton(Bundle.getMessage("ButtonOK"))); 228 ok.addActionListener((evt) -> { 229 _tableModel.setValidateModule(_validateModuleComboBox.getItemAt(_validateModuleComboBox.getSelectedIndex())._module); 230 _tableModel.setEditable(allowEdit.isSelected()); 231 _tableModel.setEditableColumns(String.join("\t", columnList.getSelectedValuesList())); 232 dialog.dispose(); 233 }); 234// p.add(panelBottom); 235 236 c.gridwidth = 3; 237 c.gridx = 0; 238 c.gridy = 99; 239 c.anchor = java.awt.GridBagConstraints.CENTER; 240 c.fill = java.awt.GridBagConstraints.NONE; 241 p.add(panelBottom, c); 242 add(p); 243 244 dialog.getContentPane().add(p); 245 dialog.pack(); 246 dialog.setModal(true); 247 dialog.setVisible(true); 248 } 249 250 @Override 251 public boolean setEditIconMenu(javax.swing.JPopupMenu popup) { 252 String txt = java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameLogixNGTable")); 253 popup.add(new javax.swing.AbstractAction(txt) { 254 @Override 255 public void actionPerformed(ActionEvent e) { 256 edit(); 257 } 258 }); 259 260 txt = Bundle.getMessage("ConfigureLogixNGTable"); 261 popup.add(new javax.swing.AbstractAction(txt) { 262 @Override 263 public void actionPerformed(ActionEvent e) { 264 configureLogixNGTable(); 265 } 266 }); 267 return true; 268 } 269 270 @Override 271 protected void edit() { 272 makeIconEditorFrame(this, "LogixNGTable", true, _iconEditor); 273 _iconEditor.setPickList(jmri.jmrit.picker.PickListModel.namedTablePickModelInstance()); 274 ActionListener addIconAction = a -> editNamedTable(); 275 _iconEditor.makeIconPanel(false); 276 _iconEditor.complete(addIconAction, false, false, true); 277 _iconEditor.setSelection(_tableModel.getTable()); 278 } 279 280 void editNamedTable() { 281 _tableModel.setTable(_iconEditor.getTableSelection().getDisplayName()); 282 _iconEditorFrame.dispose(); 283 _iconEditorFrame = null; 284 _iconEditor = null; 285 validate(); 286 } 287 288 @Override 289 void cleanup() { 290 if (_scrollPane != null) { 291 _table.removeMouseListener(_mouseListener); 292 _rowHeader.removeMouseListener(_mouseListener); 293 _scrollPane.removeMouseListener(_mouseListener); 294 } 295 _tableModel.setTable((NamedTable)null); 296 _tableModel.setValidateModule(null); 297 } 298 299 private static String getObjectAsString(Object o) { 300 return o != null ? o.toString() : ""; 301 } 302 303 @Override 304 public void remove() { 305 _tableModel.dispose(); 306 } 307 308 // ------------ Table Models ------------ 309 310 /** 311 * Table model for Tables in the Edit NamedTable pane. 312 */ 313 public final class TableModel extends AbstractTableModel 314 implements PropertyChangeListener { 315 316 private class HeaderType { 317 Class<?> _class; 318 String _type; 319 String _parameters; 320 String _header; 321 TableCellEditor _cellEditor; 322 } 323 324 private NamedBeanHandle<NamedTable> _namedTable; 325 private NamedBeanHandle<Module> _validateModule; 326 private boolean _editable = false; 327 private String _editableColumns = ""; 328 List<String> _editableColumnsList; 329 330 private final LogixNG validateModuleLogixNG = new DefaultLogixNG("IQ:JMRI:LogixNGTableIcon", null); 331 private final ConditionalNG validateModuleConditionalNG = 332 new DefaultConditionalNG("IQC:JMRI:LogixNGTableIcon", null, LogixNG_Thread.DEFAULT_LOGIXNG_THREAD); 333 private final DigitalCallModule validateModuleAction = new DigitalCallModule("IQDA:JMRI:LogixNGTableIcon", null); 334 private Map<String, Object> _variablesWithValues; 335 private final MyData _myData = new MyData(); 336 private final HeaderType[] _headers; 337 338 339 public TableModel(String tableName) { 340 initCallModule(); 341 setEditableColumns(""); 342 setTable(tableName); 343 344 if (_namedTable != null) { 345 _headers = new HeaderType[_namedTable.getBean().numColumns()]; 346 initHeaders(); 347 } else { 348 _headers = null; 349 } 350 } 351 352 private void initHeaders() { 353 for (int col=0; col < _namedTable.getBean().numColumns(); col++) { 354 HeaderType headerType = new HeaderType(); 355 _headers[col] = headerType; 356 357 Object headerObj = _namedTable.getBean().getCell(0, col+1); 358 String header; 359 if (headerObj != null) { 360 header = headerObj.toString(); 361 } else { 362 header = ""; 363 } 364 if (header.startsWith("{{{")) { 365 Pattern pattern = Pattern.compile("\\{\\{\\{(.+?)(\\:.+?)?\\}\\}\\}(.*)"); 366 Matcher matcher = pattern.matcher(header); 367 if (matcher.matches()) { 368 headerType._header = matcher.group(3); 369 headerType._parameters = matcher.group(2); 370 headerType._type = matcher.group(1).toLowerCase(); 371 switch (headerType._type) { 372 case "button": 373 headerType._class = JButton.class; 374 break; 375 case "list": 376 String comboBoxTableName = headerType._parameters.substring(1); 377 NamedTable comboBoxTable = InstanceManager 378 .getDefault(NamedTableManager.class) 379 .getNamedTable(comboBoxTableName); 380 if (comboBoxTable != null) { 381 headerType._class = JComboBox.class; 382 JComboBox<String> comboBox = new JComboBox<>(); 383 for (int row=1; row <= comboBoxTable.numRows(); row++) { 384 Object val = comboBoxTable.getCell(row, 1); 385 String value = val != null ? val.toString() : ""; 386 comboBox.addItem(value); 387 } 388 headerType._cellEditor = new DefaultCellEditor(comboBox); 389 } else { 390 log.warn("Cannot load LogixNG Table: {}", comboBoxTableName); 391 } 392 break; 393 default: 394 log.warn("Unknown column type: {}", matcher.group(1)); 395 headerType._type = null; 396 } 397 } else { 398 log.warn("Unknown column definition: {}", header); 399 } 400 } else { 401 _headers[col]._header = header; 402 } 403 } 404 } 405 406 public NamedTable getTable() { 407 return _namedTable != null ? _namedTable.getBean() : null; 408 } 409 410 public void setTable(String tableName) { 411 if (_namedTable != null) { 412 _namedTable.getBean().removePropertyChangeListener(this); 413 } 414 415 NamedTable table = null; 416 if (tableName != null) { 417 table = InstanceManager.getDefault(NamedTableManager.class) 418 .getNamedTable(tableName); 419 } 420 421 if (table != null) { 422 _namedTable = InstanceManager.getDefault(NamedBeanHandleManager.class) 423 .getNamedBeanHandle(table.getDisplayName(), table); 424 if (_namedTable != null) { 425 _namedTable.getBean().addPropertyChangeListener(this); 426 } 427 } else { 428 _namedTable = null; 429 } 430 431 fireTableStructureChanged(); 432 } 433 434 public void setTable(NamedTable table) { 435 if (table != null) { 436 _namedTable = InstanceManager.getDefault(NamedBeanHandleManager.class) 437 .getNamedBeanHandle(table.getDisplayName(), table); 438 } else { 439 _namedTable = null; 440 } 441 fireTableStructureChanged(); 442 } 443 444 public Module getValidateModule() { 445 return _validateModule != null ? _validateModule.getBean() : null; 446 } 447 448 public void setValidateModule(Module module) { 449 if (module != null) { 450 _validateModule = InstanceManager.getDefault(NamedBeanHandleManager.class) 451 .getNamedBeanHandle(module.getDisplayName(), module); 452 } else { 453 _validateModule = null; 454 } 455 } 456 457 public boolean isEditable() { 458 return _editable; 459 } 460 461 public void setEditable(boolean editable) { 462 _editable = editable; 463 } 464 465 public String getEditableColumns() { 466 return _editableColumns; 467 } 468 469 public void setEditableColumns(String editableColumns) { 470 _editableColumns = editableColumns; 471 _editableColumnsList = Arrays.asList(_editableColumns.split("\t")); 472 } 473 474 @Override 475 public int getColumnCount() { 476 if (_namedTable != null) { 477 return _namedTable.getBean().numColumns(); 478 } else { 479 return 0; 480 } 481 } 482 483 @Override 484 public int getRowCount() { 485 if (_namedTable != null) { 486 return _namedTable.getBean().numRows(); 487 } else { 488 return 0; 489 } 490 } 491 492 @Override 493 public String getColumnName(int col) { 494 return _headers[col]._header; 495 } 496 497 @Override 498 public Object getValueAt(int row, int col) { 499 if (_namedTable == null) { 500 return null; 501 } 502 return _namedTable.getBean().getCell(row+1, col+1); 503 } 504 505 @Override 506 public void setValueAt(Object val, int row, int col) { 507 callValidateModule(val, row, col); 508 } 509 510 @Override 511 public Class<?> getColumnClass(int col) { 512 Class<?> clazz = _headers[col]._class; 513 if (clazz != null) { 514 return clazz; 515 } 516 return super.getColumnClass(col); 517 } 518 519 @Override 520 public boolean isCellEditable(int rowIndex, int columnIndex) { 521 if (!_editable) { 522 return false; 523 } 524 525 String columnHeader = getObjectAsString(_namedTable.getBean().getCell(0, columnIndex+1)); 526 boolean allowColumn = _editableColumns.isEmpty(); 527 allowColumn |= _editableColumnsList.contains(columnHeader); 528 529 return allowColumn; 530 } 531 532 private void initCallModule() { 533 validateModuleLogixNG.addConditionalNG(validateModuleConditionalNG); 534 535 DigitalMany many = new DigitalMany("IQDA:JMRI:LogixNGTableIcon", null); 536 MaleSocket maleSocketMany = new DefaultMaleDigitalActionSocket( 537 InstanceManager.getDefault(DigitalActionManager.class), many); 538 many.setParent(maleSocketMany); 539 540 maleSocketMany.addLocalVariable("iconId", SymbolTable.InitialValueType.String, null); 541 maleSocketMany.addLocalVariable("tableName", SymbolTable.InitialValueType.String, null); 542 maleSocketMany.addLocalVariable("row", SymbolTable.InitialValueType.String, null); 543 maleSocketMany.addLocalVariable("column", SymbolTable.InitialValueType.String, null); 544 maleSocketMany.addLocalVariable("type", SymbolTable.InitialValueType.String, null); 545 maleSocketMany.addLocalVariable("oldValue", SymbolTable.InitialValueType.String, null); 546 maleSocketMany.addLocalVariable("newValue", SymbolTable.InitialValueType.String, null); 547 548 try { 549 validateModuleConditionalNG.getFemaleSocket().connect(maleSocketMany); 550 } catch (SocketAlreadyConnectedException e) { 551 log.error("Exception when creating error handling LogixNG: ", e); 552 } 553 554 SetLocalVariables setLocalVariables = new SetLocalVariables("IQDA:JMRI:LogixNGTableIcon", null); 555 MaleSocket maleSocketSetLocalVariables = new DefaultMaleDigitalActionSocket( 556 InstanceManager.getDefault(DigitalActionManager.class), setLocalVariables); 557 setLocalVariables.setParent(maleSocketSetLocalVariables); 558 _variablesWithValues = setLocalVariables.getMap(); 559 560 try { 561 maleSocketMany.getChild(maleSocketMany.getChildCount()-1).connect(maleSocketSetLocalVariables); 562 } catch (SocketAlreadyConnectedException e) { 563 log.error("Exception when creating error handling LogixNG: ", e); 564 } 565 566 RunFinally runFinally = new RunFinally("IQDA:JMRI:LogixNGTableIcon", null, this::handleResult, _myData); 567 MaleSocket maleSocketRunFinally = new DefaultMaleDigitalActionSocket( 568 InstanceManager.getDefault(DigitalActionManager.class), runFinally); 569 runFinally.setParent(maleSocketRunFinally); 570 571 try { 572 maleSocketMany.getChild(maleSocketMany.getChildCount()-1).connect(maleSocketRunFinally); 573 } catch (SocketAlreadyConnectedException e) { 574 log.error("Exception when creating error handling LogixNG: ", e); 575 } 576 577 validateModuleAction.addParameter("__iconId__", SymbolTable.InitialValueType.LocalVariable, "iconId", Module.ReturnValueType.None, null); 578 validateModuleAction.addParameter("__tableName__", SymbolTable.InitialValueType.LocalVariable, "tableName", Module.ReturnValueType.None, null); 579 validateModuleAction.addParameter("__row__", SymbolTable.InitialValueType.LocalVariable, "row", Module.ReturnValueType.None, null); 580 validateModuleAction.addParameter("__column__", SymbolTable.InitialValueType.LocalVariable, "column", Module.ReturnValueType.None, null); 581 validateModuleAction.addParameter("__type__", SymbolTable.InitialValueType.LocalVariable, "type", Module.ReturnValueType.None, null); 582 validateModuleAction.addParameter("__oldValue__", SymbolTable.InitialValueType.LocalVariable, "oldValue", Module.ReturnValueType.None, null); 583 validateModuleAction.addParameter("__newValue__", SymbolTable.InitialValueType.LocalVariable, "newValue", Module.ReturnValueType.LocalVariable, "newValue"); 584 MaleSocket maleSocket = new DefaultMaleDigitalActionSocket(InstanceManager.getDefault(DigitalActionManager.class), validateModuleAction); 585 validateModuleAction.setParent(maleSocket); 586 try { 587 runFinally.getChild(0).connect(maleSocket); 588 } catch (SocketAlreadyConnectedException e) { 589 log.error("Exception when creating error handling LogixNG: ", e); 590 } 591 List<String> errors = new ArrayList<>(); 592 validateModuleLogixNG.setParentForAllChildren(errors); 593 if (!errors.isEmpty()) { 594 for (String s : errors) { 595 log.error("Error: {}", s); 596 } 597 } 598 } 599 600 private void callValidateModule(Object val, int row, int col) { 601 if (_validateModule == null || 602 !_validateModule.getBean().getRootSocket().isConnected()) { 603 _namedTable.getBean().setCell(val, row+1, col+1); 604 return; 605 } 606 validateModuleAction.getSelectNamedBean().setNamedBean(_validateModule); 607 608 NamedTable table = _namedTable.getBean(); 609 _variablesWithValues.put("iconId", LogixNGTableIcon.this.getId()); 610 _variablesWithValues.put("tableName", _namedTable.getName()); 611 _variablesWithValues.put("row", table.getCell(row+1, 0)); 612 _variablesWithValues.put("column", table.getCell(0, col+1)); 613 _variablesWithValues.put("type", _headers[col]._type); 614 _variablesWithValues.put("oldValue", table.getCell(row+1, col+1)); 615 _variablesWithValues.put("newValue", val); 616 _myData.setValues(_namedTable.getBean(), row, col); 617 validateModuleConditionalNG.execute(); 618 } 619 620 private void handleResult(ConditionalNG conditionalNG, Exception ex, RunFinally.Data data) { 621 if (ex != null) { 622 if (ex instanceof ValidationErrorException) { 623 JOptionPane.showMessageDialog(LogixNGTableIcon.this, 624 ex.getMessage(), 625 Bundle.getMessage("LogixNGTableIcon_ValidationError"), 626 JOptionPane.ERROR_MESSAGE); 627 } else { 628 JOptionPane.showMessageDialog(LogixNGTableIcon.this, 629 ex.getMessage(), 630 Bundle.getMessage("LogixNGTableIcon_Error"), 631 JOptionPane.ERROR_MESSAGE); 632 } 633 } else { 634 if (!(data instanceof MyData)) { 635 throw new IllegalArgumentException("data is not a MyData"); 636 } 637 MyData myData = (MyData)data; 638 Object newValue = conditionalNG.getSymbolTable().getValue("newValue"); 639 myData._namedTable.setCell(newValue, myData._row+1, myData._col+1); 640 TableModel.this.fireTableCellUpdated(myData._row, myData._col); 641 } 642 } 643 644 @Override 645 public void propertyChange(PropertyChangeEvent evt) { 646 if (evt instanceof NamedTablePropertyChangeEvent) { 647 var tableEvt = (NamedTablePropertyChangeEvent)evt; 648 if (NamedTable.PROPERTY_CELL_CHANGED.equals(evt.getPropertyName())) { 649 fireTableCellUpdated(tableEvt.getRow()-1, tableEvt.getColumn()-1); 650 } 651 } 652 } 653 654 private void dispose() { 655 if (_namedTable != null) { 656 _namedTable.getBean().removePropertyChangeListener(this); 657 } 658 } 659 660 } 661 662 663 private static class MyData implements RunFinally.Data { 664 665 private NamedTable _namedTable; 666 private int _row; 667 private int _col; 668 669 private void setValues(NamedTable namedTable, int row, int col) { 670 this._namedTable = namedTable; 671 this._row = row; 672 this._col = col; 673 } 674 } 675 676 677 private class RowHeaderListModel extends AbstractListModel<Object> { 678 @Override 679 public int getSize() { 680 return _tableModel.getTable().numRows(); 681 } 682 683 @Override 684 public Object getElementAt(int index) { 685 // Ensure the header has at least five characters and ensure 686 // there are at least two spaces at the end since the last letter 687 // doesn't fully fit at the row. 688 Object data = _tableModel.getTable().getCell(index+1, 0); 689 String padding = " "; // Two spaces 690 String str = data != null ? data.toString().concat(padding) : padding; 691 return str.length() < 5 ? str.concat(" ").substring(0, 7) : str; 692 } 693 694 } 695 696 697 private static final class RowHeaderRenderer extends JLabel implements ListCellRenderer<Object> { 698 699 RowHeaderRenderer(JTable table) { 700 JTableHeader header = table.getTableHeader(); 701 setOpaque(true); 702 setBorder(UIManager.getBorder("TableHeader.cellBorder")); 703 setHorizontalAlignment(CENTER); 704 setForeground(header.getForeground()); 705 setBackground(header.getBackground()); 706 setFont(header.getFont()); 707 } 708 709 @Override 710 public Component getListCellRendererComponent(JList<?> list, Object value, 711 int index, boolean isSelected, boolean cellHasFocus) { 712 setText((value == null) ? "" : value.toString()); 713 return this; 714 } 715 } 716 717 718 private static class ModuleItem { 719 720 private final Module _module; 721 722 public ModuleItem(Module m) { 723 _module = m; 724 } 725 726 @Override 727 public String toString() { 728 if (_module == null) return ""; 729 else return _module.getDisplayName(); 730 } 731 } 732 733 734 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogixNGTableIcon.class); 735}