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