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 super.remove(); 307 } 308 309 // ------------ Table Models ------------ 310 311 /** 312 * Table model for Tables in the Edit NamedTable pane. 313 */ 314 public final class TableModel extends AbstractTableModel 315 implements PropertyChangeListener { 316 317 private class HeaderType { 318 Class<?> _class; 319 String _type; 320 String _parameters; 321 String _header; 322 TableCellEditor _cellEditor; 323 } 324 325 private NamedBeanHandle<NamedTable> _namedTable; 326 private NamedBeanHandle<Module> _validateModule; 327 private boolean _editable = false; 328 private String _editableColumns = ""; 329 List<String> _editableColumnsList; 330 331 private final LogixNG validateModuleLogixNG = new DefaultLogixNG("IQ:JMRI:LogixNGTableIcon", null); 332 private final ConditionalNG validateModuleConditionalNG = 333 new DefaultConditionalNG("IQC:JMRI:LogixNGTableIcon", null, LogixNG_Thread.DEFAULT_LOGIXNG_THREAD); 334 private final DigitalCallModule validateModuleAction = new DigitalCallModule("IQDA:JMRI:LogixNGTableIcon", null); 335 private Map<String, Object> _variablesWithValues; 336 private final MyData _myData = new MyData(); 337 private final HeaderType[] _headers; 338 339 340 public TableModel(String tableName) { 341 initCallModule(); 342 setEditableColumns(""); 343 setTable(tableName); 344 345 if (_namedTable != null) { 346 _headers = new HeaderType[_namedTable.getBean().numColumns()]; 347 initHeaders(); 348 } else { 349 _headers = null; 350 } 351 } 352 353 private void initHeaders() { 354 for (int col=0; col < _namedTable.getBean().numColumns(); col++) { 355 HeaderType headerType = new HeaderType(); 356 _headers[col] = headerType; 357 358 Object headerObj = _namedTable.getBean().getCell(0, col+1); 359 String header; 360 if (headerObj != null) { 361 header = headerObj.toString(); 362 } else { 363 header = ""; 364 } 365 if (header.startsWith("{{{")) { 366 Pattern pattern = Pattern.compile("\\{\\{\\{(.+?)(\\:.+?)?\\}\\}\\}(.*)"); 367 Matcher matcher = pattern.matcher(header); 368 if (matcher.matches()) { 369 headerType._header = matcher.group(3); 370 headerType._parameters = matcher.group(2); 371 headerType._type = matcher.group(1).toLowerCase(); 372 switch (headerType._type) { 373 case "button": 374 headerType._class = JButton.class; 375 break; 376 case "list": 377 String comboBoxTableName = headerType._parameters.substring(1); 378 NamedTable comboBoxTable = InstanceManager 379 .getDefault(NamedTableManager.class) 380 .getNamedTable(comboBoxTableName); 381 if (comboBoxTable != null) { 382 headerType._class = JComboBox.class; 383 JComboBox<String> comboBox = new JComboBox<>(); 384 for (int row=1; row <= comboBoxTable.numRows(); row++) { 385 Object val = comboBoxTable.getCell(row, 1); 386 String value = val != null ? val.toString() : ""; 387 comboBox.addItem(value); 388 } 389 headerType._cellEditor = new DefaultCellEditor(comboBox); 390 } else { 391 log.warn("Cannot load LogixNG Table: {}", comboBoxTableName); 392 } 393 break; 394 default: 395 log.warn("Unknown column type: {}", matcher.group(1)); 396 headerType._type = null; 397 } 398 } else { 399 log.warn("Unknown column definition: {}", header); 400 } 401 } else { 402 _headers[col]._header = header; 403 } 404 } 405 } 406 407 public NamedTable getTable() { 408 return _namedTable != null ? _namedTable.getBean() : null; 409 } 410 411 public void setTable(String tableName) { 412 if (_namedTable != null) { 413 _namedTable.getBean().removePropertyChangeListener(this); 414 } 415 416 NamedTable table = null; 417 if (tableName != null) { 418 table = InstanceManager.getDefault(NamedTableManager.class) 419 .getNamedTable(tableName); 420 } 421 422 if (table != null) { 423 _namedTable = InstanceManager.getDefault(NamedBeanHandleManager.class) 424 .getNamedBeanHandle(table.getDisplayName(), table); 425 if (_namedTable != null) { 426 _namedTable.getBean().addPropertyChangeListener(this); 427 } 428 } else { 429 _namedTable = null; 430 } 431 432 fireTableStructureChanged(); 433 } 434 435 public void setTable(NamedTable table) { 436 if (table != null) { 437 _namedTable = InstanceManager.getDefault(NamedBeanHandleManager.class) 438 .getNamedBeanHandle(table.getDisplayName(), table); 439 } else { 440 _namedTable = null; 441 } 442 fireTableStructureChanged(); 443 } 444 445 public Module getValidateModule() { 446 return _validateModule != null ? _validateModule.getBean() : null; 447 } 448 449 public void setValidateModule(Module module) { 450 if (module != null) { 451 _validateModule = InstanceManager.getDefault(NamedBeanHandleManager.class) 452 .getNamedBeanHandle(module.getDisplayName(), module); 453 } else { 454 _validateModule = null; 455 } 456 } 457 458 public boolean isEditable() { 459 return _editable; 460 } 461 462 public void setEditable(boolean editable) { 463 _editable = editable; 464 } 465 466 public String getEditableColumns() { 467 return _editableColumns; 468 } 469 470 public void setEditableColumns(String editableColumns) { 471 _editableColumns = editableColumns; 472 _editableColumnsList = Arrays.asList(_editableColumns.split("\t")); 473 } 474 475 @Override 476 public int getColumnCount() { 477 if (_namedTable != null) { 478 return _namedTable.getBean().numColumns(); 479 } else { 480 return 0; 481 } 482 } 483 484 @Override 485 public int getRowCount() { 486 if (_namedTable != null) { 487 return _namedTable.getBean().numRows(); 488 } else { 489 return 0; 490 } 491 } 492 493 @Override 494 public String getColumnName(int col) { 495 return _headers[col]._header; 496 } 497 498 @Override 499 public Object getValueAt(int row, int col) { 500 if (_namedTable == null) { 501 return null; 502 } 503 return _namedTable.getBean().getCell(row+1, col+1); 504 } 505 506 @Override 507 public void setValueAt(Object val, int row, int col) { 508 callValidateModule(val, row, col); 509 } 510 511 @Override 512 public Class<?> getColumnClass(int col) { 513 Class<?> clazz = _headers[col]._class; 514 if (clazz != null) { 515 return clazz; 516 } 517 return super.getColumnClass(col); 518 } 519 520 @Override 521 public boolean isCellEditable(int rowIndex, int columnIndex) { 522 if (!_editable) { 523 return false; 524 } 525 526 String columnHeader = getObjectAsString(_namedTable.getBean().getCell(0, columnIndex+1)); 527 boolean allowColumn = _editableColumns.isEmpty(); 528 allowColumn |= _editableColumnsList.contains(columnHeader); 529 530 return allowColumn; 531 } 532 533 private void initCallModule() { 534 validateModuleLogixNG.addConditionalNG(validateModuleConditionalNG); 535 536 DigitalMany many = new DigitalMany("IQDA:JMRI:LogixNGTableIcon", null); 537 MaleSocket maleSocketMany = new DefaultMaleDigitalActionSocket( 538 InstanceManager.getDefault(DigitalActionManager.class), many); 539 many.setParent(maleSocketMany); 540 541 maleSocketMany.addLocalVariable("iconId", SymbolTable.InitialValueType.String, null); 542 maleSocketMany.addLocalVariable("tableName", SymbolTable.InitialValueType.String, null); 543 maleSocketMany.addLocalVariable("row", SymbolTable.InitialValueType.String, null); 544 maleSocketMany.addLocalVariable("column", SymbolTable.InitialValueType.String, null); 545 maleSocketMany.addLocalVariable("type", SymbolTable.InitialValueType.String, null); 546 maleSocketMany.addLocalVariable("oldValue", SymbolTable.InitialValueType.String, null); 547 maleSocketMany.addLocalVariable("newValue", SymbolTable.InitialValueType.String, null); 548 549 try { 550 validateModuleConditionalNG.getFemaleSocket().connect(maleSocketMany); 551 } catch (SocketAlreadyConnectedException e) { 552 log.error("Exception when creating error handling LogixNG: ", e); 553 } 554 555 SetLocalVariables setLocalVariables = new SetLocalVariables("IQDA:JMRI:LogixNGTableIcon", null); 556 MaleSocket maleSocketSetLocalVariables = new DefaultMaleDigitalActionSocket( 557 InstanceManager.getDefault(DigitalActionManager.class), setLocalVariables); 558 setLocalVariables.setParent(maleSocketSetLocalVariables); 559 _variablesWithValues = setLocalVariables.getMap(); 560 561 try { 562 maleSocketMany.getChild(maleSocketMany.getChildCount()-1).connect(maleSocketSetLocalVariables); 563 } catch (SocketAlreadyConnectedException e) { 564 log.error("Exception when creating error handling LogixNG: ", e); 565 } 566 567 RunFinally runFinally = new RunFinally("IQDA:JMRI:LogixNGTableIcon", null, this::handleResult, _myData); 568 MaleSocket maleSocketRunFinally = new DefaultMaleDigitalActionSocket( 569 InstanceManager.getDefault(DigitalActionManager.class), runFinally); 570 runFinally.setParent(maleSocketRunFinally); 571 572 try { 573 maleSocketMany.getChild(maleSocketMany.getChildCount()-1).connect(maleSocketRunFinally); 574 } catch (SocketAlreadyConnectedException e) { 575 log.error("Exception when creating error handling LogixNG: ", e); 576 } 577 578 validateModuleAction.addParameter("__iconId__", SymbolTable.InitialValueType.LocalVariable, "iconId", Module.ReturnValueType.None, null); 579 validateModuleAction.addParameter("__tableName__", SymbolTable.InitialValueType.LocalVariable, "tableName", Module.ReturnValueType.None, null); 580 validateModuleAction.addParameter("__row__", SymbolTable.InitialValueType.LocalVariable, "row", Module.ReturnValueType.None, null); 581 validateModuleAction.addParameter("__column__", SymbolTable.InitialValueType.LocalVariable, "column", Module.ReturnValueType.None, null); 582 validateModuleAction.addParameter("__type__", SymbolTable.InitialValueType.LocalVariable, "type", Module.ReturnValueType.None, null); 583 validateModuleAction.addParameter("__oldValue__", SymbolTable.InitialValueType.LocalVariable, "oldValue", Module.ReturnValueType.None, null); 584 validateModuleAction.addParameter("__newValue__", SymbolTable.InitialValueType.LocalVariable, "newValue", Module.ReturnValueType.LocalVariable, "newValue"); 585 MaleSocket maleSocket = new DefaultMaleDigitalActionSocket(InstanceManager.getDefault(DigitalActionManager.class), validateModuleAction); 586 validateModuleAction.setParent(maleSocket); 587 try { 588 runFinally.getChild(0).connect(maleSocket); 589 } catch (SocketAlreadyConnectedException e) { 590 log.error("Exception when creating error handling LogixNG: ", e); 591 } 592 List<String> errors = new ArrayList<>(); 593 validateModuleLogixNG.setParentForAllChildren(errors); 594 if (!errors.isEmpty()) { 595 for (String s : errors) { 596 log.error("Error: {}", s); 597 } 598 } 599 } 600 601 private void callValidateModule(Object val, int row, int col) { 602 if (_validateModule == null || 603 !_validateModule.getBean().getRootSocket().isConnected()) { 604 _namedTable.getBean().setCell(val, row+1, col+1); 605 return; 606 } 607 validateModuleAction.getSelectNamedBean().setNamedBean(_validateModule); 608 609 NamedTable table = _namedTable.getBean(); 610 _variablesWithValues.put("iconId", LogixNGTableIcon.this.getId()); 611 _variablesWithValues.put("tableName", _namedTable.getName()); 612 _variablesWithValues.put("row", table.getCell(row+1, 0)); 613 _variablesWithValues.put("column", table.getCell(0, col+1)); 614 _variablesWithValues.put("type", _headers[col]._type); 615 _variablesWithValues.put("oldValue", table.getCell(row+1, col+1)); 616 _variablesWithValues.put("newValue", val); 617 _myData.setValues(_namedTable.getBean(), row, col); 618 validateModuleConditionalNG.execute(); 619 } 620 621 private void handleResult(ConditionalNG conditionalNG, Exception ex, RunFinally.Data data) { 622 if (ex != null) { 623 if (ex instanceof ValidationErrorException) { 624 JOptionPane.showMessageDialog(LogixNGTableIcon.this, 625 ex.getMessage(), 626 Bundle.getMessage("LogixNGTableIcon_ValidationError"), 627 JOptionPane.ERROR_MESSAGE); 628 } else { 629 JOptionPane.showMessageDialog(LogixNGTableIcon.this, 630 ex.getMessage(), 631 Bundle.getMessage("LogixNGTableIcon_Error"), 632 JOptionPane.ERROR_MESSAGE); 633 } 634 } else { 635 if (!(data instanceof MyData)) { 636 throw new IllegalArgumentException("data is not a MyData"); 637 } 638 MyData myData = (MyData)data; 639 Object newValue = conditionalNG.getSymbolTable().getValue("newValue"); 640 myData._namedTable.setCell(newValue, myData._row+1, myData._col+1); 641 TableModel.this.fireTableCellUpdated(myData._row, myData._col); 642 } 643 } 644 645 @Override 646 public void propertyChange(PropertyChangeEvent evt) { 647 if (evt instanceof NamedTablePropertyChangeEvent) { 648 var tableEvt = (NamedTablePropertyChangeEvent)evt; 649 if (NamedTable.PROPERTY_CELL_CHANGED.equals(evt.getPropertyName())) { 650 fireTableCellUpdated(tableEvt.getRow()-1, tableEvt.getColumn()-1); 651 } 652 } 653 } 654 655 private void dispose() { 656 if (_namedTable != null) { 657 _namedTable.getBean().removePropertyChangeListener(this); 658 } 659 } 660 661 } 662 663 664 private static class MyData implements RunFinally.Data { 665 666 private NamedTable _namedTable; 667 private int _row; 668 private int _col; 669 670 private void setValues(NamedTable namedTable, int row, int col) { 671 this._namedTable = namedTable; 672 this._row = row; 673 this._col = col; 674 } 675 } 676 677 678 private class RowHeaderListModel extends AbstractListModel<Object> { 679 @Override 680 public int getSize() { 681 return _tableModel.getTable().numRows(); 682 } 683 684 @Override 685 public Object getElementAt(int index) { 686 // Ensure the header has at least five characters and ensure 687 // there are at least two spaces at the end since the last letter 688 // doesn't fully fit at the row. 689 Object data = _tableModel.getTable().getCell(index+1, 0); 690 String padding = " "; // Two spaces 691 String str = data != null ? data.toString().concat(padding) : padding; 692 return str.length() < 5 ? str.concat(" ").substring(0, 7) : str; 693 } 694 695 } 696 697 698 private static final class RowHeaderRenderer extends JLabel implements ListCellRenderer<Object> { 699 700 RowHeaderRenderer(JTable table) { 701 JTableHeader header = table.getTableHeader(); 702 setOpaque(true); 703 setBorder(UIManager.getBorder("TableHeader.cellBorder")); 704 setHorizontalAlignment(CENTER); 705 setForeground(header.getForeground()); 706 setBackground(header.getBackground()); 707 setFont(header.getFont()); 708 } 709 710 @Override 711 public Component getListCellRendererComponent(JList<?> list, Object value, 712 int index, boolean isSelected, boolean cellHasFocus) { 713 setText((value == null) ? "" : value.toString()); 714 return this; 715 } 716 } 717 718 719 private static class ModuleItem { 720 721 private final Module _module; 722 723 public ModuleItem(Module m) { 724 _module = m; 725 } 726 727 @Override 728 public String toString() { 729 if (_module == null) return ""; 730 else return _module.getDisplayName(); 731 } 732 } 733 734 735 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogixNGTableIcon.class); 736}