001package jmri.jmrit.beantable; 002 003import java.awt.Container; 004import java.awt.FlowLayout; 005import java.awt.GridBagConstraints; 006import java.awt.GridBagLayout; 007import java.awt.event.ActionEvent; 008import java.awt.event.ItemEvent; 009import java.beans.PropertyVetoException; 010import javax.swing.*; 011import javax.swing.filechooser.FileNameExtensionFilter; 012 013import jmri.InstanceManager; 014import jmri.Manager; 015import jmri.jmrit.logixng.Table; 016import jmri.util.FileUtil; 017import jmri.util.JmriJFrame; 018 019import jmri.jmrit.logixng.Base; 020import jmri.jmrit.logixng.NamedTable; 021import jmri.jmrit.logixng.NamedTableManager; 022import jmri.jmrit.logixng.tools.swing.AbstractLogixNGEditor; 023import jmri.jmrit.logixng.tools.swing.TableEditor; 024 025/** 026 * Swing action to create and register a LogixNG Table. 027 * <p> 028 Also contains the panes to create, edit, and delete a LogixNG. 029 <p> 030 * Most of the text used in this GUI is in BeanTableBundle.properties, accessed 031 * via Bundle.getMessage(). 032 * 033 * @author Dave Duchamp Copyright (C) 2007 (LogixTableAction) 034 * @author Pete Cressman Copyright (C) 2009, 2010, 2011 (LogixTableAction) 035 * @author Matthew Harris copyright (c) 2009 (LogixTableAction) 036 * @author Dave Sand copyright (c) 2017 (LogixTableAction) 037 * @author Daniel Bergqvist copyright (c) 2019 038 * @author Dave Sand copyright (c) 2021 039 * @author J. Scott Walton (c) 2022 (Csv types) 040 */ 041public class LogixNGTableTableAction extends AbstractLogixNGTableAction<NamedTable> { 042 043 JRadioButton _typeExternalTable = new JRadioButton(Bundle.getMessage("LogixNG_typeExternalTable")); 044 JRadioButton _typeInternalTable = new JRadioButton(Bundle.getMessage("LogixNG_typeInternalTable")); 045 ButtonGroup _buttonGroup = new ButtonGroup(); 046 JTextField _csvFileName = new JTextField(50); 047 048 ButtonGroup _csvGroup = new ButtonGroup(); 049 JRadioButton _csvTabbed = new JRadioButton(Table.CsvType.TABBED.toString()); 050 JRadioButton _csvComma = new JRadioButton(Table.CsvType.COMMA.toString()); 051 JRadioButton _csvSemicolon = new JRadioButton(Table.CsvType.SEMICOLON.toString()); 052 053 JLabel _csvLabel = new JLabel(Bundle.getMessage("LogixNG_CsvType") + ":"); 054 /** 055 * Create a LogixNGTableAction instance. 056 * 057 * @param s the Action title, not the title of the resulting frame. Perhaps 058 * this should be changed? 059 */ 060 public LogixNGTableTableAction(String s) { 061 super(s); 062 } 063 064 /** 065 * Create a LogixNGTableAction instance with default title. 066 */ 067 public LogixNGTableTableAction() { 068 this(Bundle.getMessage("TitleLogixNGTableTable")); 069 } 070 071 @Override 072 protected void setTitle() { 073 f.setTitle(Bundle.getMessage("TitleLogixNGTableTable")); 074 } 075 076 @Override 077 public String getClassDescription() { 078 return Bundle.getMessage("TitleLogixNGTableTable"); // NOI18N 079 } 080 081 @Override 082 protected AbstractLogixNGEditor<NamedTable> getEditor(BeanTableDataModel<NamedTable> m, String sName) { 083 return new TableEditor(m, sName); 084 } 085 086 @Override 087 protected Manager<NamedTable> getManager() { 088 return InstanceManager.getDefault(NamedTableManager.class); 089 } 090 091 @Override 092 protected void enableAll(boolean enable) { 093 // Not used by the tables table 094 } 095 096 @Override 097 protected void setEnabled(NamedTable bean, boolean enable) { 098 // Not used by the tables table 099 } 100 101 @Override 102 protected boolean isEnabled(NamedTable bean) { 103 return true; 104 } 105 106 @Override 107 protected NamedTable createBean(String userName) { 108 String systemName = InstanceManager.getDefault(NamedTableManager.class).getAutoSystemName(); 109 return createBean(systemName, userName); 110 } 111 112 @Override 113 protected NamedTable createBean(String systemName, String userName) { 114 if (_typeExternalTable.isSelected()) { 115 String fileName = _csvFileName.getText(); 116 if (fileName == null || fileName.isEmpty()) { 117 jmri.util.swing.JmriJOptionPane.showMessageDialog(addLogixNGFrame, 118 Bundle.getMessage("LogixNGError2"), Bundle.getMessage("ErrorTitle"), // NOI18N 119 jmri.util.swing.JmriJOptionPane.ERROR_MESSAGE); 120 return null; 121 } 122 if (_csvTabbed.isSelected()) { 123 return InstanceManager.getDefault(NamedTableManager.class) 124 .newCSVTable(systemName, userName, fileName, Table.CsvType.TABBED); 125 } else if (_csvComma.isSelected()) { 126 return InstanceManager.getDefault(NamedTableManager.class) 127 .newCSVTable(systemName, userName, fileName, Table.CsvType.COMMA); 128 } else if (_csvSemicolon.isSelected()) { 129 return InstanceManager.getDefault(NamedTableManager.class) 130 .newCSVTable(systemName, userName, fileName, Table.CsvType.SEMICOLON); 131 } 132 } else if (_typeInternalTable.isSelected()) { 133 // Open table editor 134 } else { 135 log.error("No table type selected"); 136 throw new RuntimeException("No table type selected"); 137 } 138 139// InstanceManager.getDefault(NamedTableManager.class).loadTableFromCSV(file, systemName, userName); 140 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. 141 } 142 143 @Override 144 protected void deleteBean(NamedTable bean) { 145 try { 146 InstanceManager.getDefault(NamedTableManager.class).deleteBean(bean, "DoDelete"); 147 } catch (PropertyVetoException e) { 148 //At this stage the DoDelete shouldn't fail, as we have already done a can delete, which would trigger a veto 149 log.error("{} : Could not Delete.", e.getMessage()); 150 } 151 } 152 153 @Override 154 protected boolean browseMonoSpace() { 155 return true; 156 } 157 158 @Override 159 protected String getBeanText(NamedTable bean, Base.PrintTreeSettings printTreeSettings) { 160 int maxColumnWidth = 0; 161 int columnWidth[] = new int[bean.numColumns()+1]; 162 String[][] cells = new String[bean.numRows()+1][]; 163 for (int row=0; row <= bean.numRows(); row++) { 164 cells[row] = new String[bean.numColumns()+1]; 165 for (int col=0; col <= bean.numColumns(); col++) { 166 Object value = bean.getCell(row, col); 167 cells[row][col] = value != null ? value.toString() : "<null>"; 168 columnWidth[col] = Math.max(columnWidth[col], cells[row][col].length()); 169 maxColumnWidth = Math.max(maxColumnWidth, columnWidth[col]); 170 } 171 } 172 String columnLine = "-".repeat(maxColumnWidth+2); 173 String columnPadding = " ".repeat(maxColumnWidth); 174 StringBuilder sb = new StringBuilder(); 175 sb.append("+"); 176 for (int col=0; col <= bean.numColumns(); col++) { 177 sb.append(columnLine.substring(0,columnWidth[col]+2)); 178 sb.append("+"); 179 if (col == bean.numColumns()) sb.append(String.format("%n")); 180 } 181 for (int row=0; row <= bean.numRows(); row++) { 182 sb.append("|"); 183 for (int col=0; col <= bean.numColumns(); col++) { 184 sb.append(" "); 185 sb.append((cells[row][col]+columnPadding).substring(0,columnWidth[col])); 186 sb.append(" |"); 187 if (col == bean.numColumns()) sb.append(String.format("%n")); 188 } 189 sb.append("+"); 190 for (int col=0; col <= bean.numColumns(); col++) { 191 sb.append(columnLine.substring(0,columnWidth[col]+2)); 192 sb.append("+"); 193 if (col == bean.numColumns()) sb.append(String.format("%n")); 194 } 195 } 196 return sb.toString(); 197 } 198 199 @Override 200 protected String getBrowserTitle() { 201 return Bundle.getMessage("LogixNG_Table_Browse_Title"); 202 } 203 204 @Override 205 protected String getAddTitleKey() { 206 return "TitleLogixNGTableTable"; 207 } 208 209 @Override 210 protected String getCreateButtonHintKey() { 211 return "LogixNGTableCreateButtonHint"; 212 } 213 214 @Override 215 protected String helpTarget() { 216 return "package.jmri.jmrit.beantable.LogixNGTableTable"; // NOI18N 217 } 218 219 private JButton createFileChooser() { 220 JButton selectFileButton = new JButton("..."); // "File" replaced by ... 221 selectFileButton.setMaximumSize(selectFileButton.getPreferredSize()); 222 selectFileButton.setToolTipText(Bundle.getMessage("LogixNG_FileButtonHint")); // NOI18N 223 selectFileButton.addActionListener((ActionEvent e) -> { 224 JFileChooser csvFileChooser = new jmri.util.swing.JmriJFileChooser(FileUtil.getUserFilesPath()); 225 csvFileChooser.setFileFilter(new FileNameExtensionFilter("CSV files", "csv", "txt", "tsv")); // NOI18N 226 csvFileChooser.rescanCurrentDirectory(); 227 int retVal = csvFileChooser.showOpenDialog(null); 228 // handle selection or cancel 229 if (retVal == JFileChooser.APPROVE_OPTION) { 230 // set selected file location 231 try { 232 _csvFileName.setText(FileUtil.getPortableFilename(csvFileChooser.getSelectedFile().getCanonicalPath())); 233 } catch (java.io.IOException ex) { 234 log.error("exception setting file location", ex); // NOI18N 235 _csvFileName.setText(""); 236 } 237 } 238 }); 239 return selectFileButton; 240 } 241 242 /** 243 * Create or copy bean frame. 244 * 245 * @param titleId property key to fetch as title of the frame (using Bundle) 246 * @param startMessageId part 1 of property key to fetch as user instruction on 247 * pane, either 1 or 2 is added to form the whole key 248 * @return the button JPanel 249 */ 250 @Override 251 protected JPanel makeAddFrame(String titleId, String startMessageId) { 252 addLogixNGFrame = new JmriJFrame(Bundle.getMessage(titleId)); 253 addLogixNGFrame.addHelpMenu( 254 "package.jmri.jmrit.beantable.LogixNGTableTable", true); // NOI18N 255 addLogixNGFrame.setLocation(50, 30); 256 Container contentPane = addLogixNGFrame.getContentPane(); 257 contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); 258 259 JPanel p; 260 p = new JPanel(); 261 p.setLayout(new FlowLayout()); 262 p.setLayout(new GridBagLayout()); 263 GridBagConstraints c = new GridBagConstraints(); 264 c.gridwidth = 1; 265 c.gridheight = 1; 266 267 c.gridx = 0; 268 c.gridy = 0; 269 c.anchor = GridBagConstraints.EAST; 270 271 p.add(_sysNameLabel, c); 272 _sysNameLabel.setLabelFor(_systemName); 273 c.gridy = 1; 274 p.add(_userNameLabel, c); 275 _userNameLabel.setLabelFor(_addUserName); 276 c.gridy = 2; 277 _csvLabel.setLabelFor(null); 278 p.add(_csvLabel, c); 279 JPanel csvPanel = new JPanel(); 280 csvPanel.setLayout(new FlowLayout()); 281 _csvGroup.add(_csvTabbed); 282 _csvGroup.add(_csvComma); 283 _csvGroup.add(_csvSemicolon); 284 _csvTabbed.setSelected(true); 285 csvPanel.add(_csvTabbed); 286 csvPanel.add(_csvComma); 287 csvPanel.add(_csvSemicolon); 288 c.gridx = 1; 289 p.add(csvPanel,c); 290 c.gridx = 0; 291 c.gridy = 3; 292 p.add(new JLabel(Bundle.getMessage("LogixNG_CsvFileName")), c); 293 294 c.gridx = 1; 295 c.gridy = 0; 296 c.anchor = GridBagConstraints.WEST; 297 c.weightx = 1.0; 298 c.fill = GridBagConstraints.HORIZONTAL; // text field will expand 299 p.add(_systemName, c); 300 c.gridy = 1; 301 p.add(_addUserName, c); 302 303 c.gridy = 3; 304 createFileChooser(); 305 p.add(createFileChooser(), c); 306 307 c.gridx = 2; // make room for file selector 308 c.gridwidth = GridBagConstraints.REMAINDER; 309 p.add(_csvFileName, c); 310 311 c.gridwidth = 1; 312 c.gridx = 2; 313 c.gridy = 1; 314 c.anchor = GridBagConstraints.WEST; 315 c.weightx = 1.0; 316 c.fill = GridBagConstraints.HORIZONTAL; // text field will expand 317 c.gridy = 0; 318 p.add(_autoSystemName, c); 319 320 321 _buttonGroup.add(_typeExternalTable); 322 _buttonGroup.add(_typeInternalTable); 323 _typeExternalTable.setSelected(true); 324 _typeInternalTable.setEnabled(false); 325 326 _addUserName.setToolTipText(Bundle.getMessage("LogixNGUserNameHint")); // NOI18N 327 _systemName.setToolTipText(Bundle.getMessage("LogixNGSystemNameHint")); // NOI18N 328 contentPane.add(p); 329 330 JPanel panel98 = new JPanel(); 331 panel98.setLayout(new FlowLayout()); 332 JPanel panel99 = new JPanel(); 333 panel99.setLayout(new BoxLayout(panel99, BoxLayout.Y_AXIS)); 334 panel99.add(_typeExternalTable, c); 335 panel99.add(_typeInternalTable, c); 336 panel98.add(panel99); 337 contentPane.add(panel98); 338 339 // set up message 340 JPanel panel3 = new JPanel(); 341 panel3.setLayout(new BoxLayout(panel3, BoxLayout.Y_AXIS)); 342 JPanel panel31 = new JPanel(); 343 panel31.setLayout(new FlowLayout()); 344 JLabel message1 = new JLabel(Bundle.getMessage(startMessageId + "LogixNGTableMessage1")); // NOI18N 345 panel31.add(message1); 346 JPanel panel32 = new JPanel(); 347 JLabel message2 = new JLabel(Bundle.getMessage(startMessageId + "LogixNGTableMessage2")); // NOI18N 348 panel32.add(message2); 349 panel3.add(panel31); 350 panel3.add(panel32); 351 contentPane.add(panel3); 352 353 // set up create and cancel buttons 354 JPanel panel5 = new JPanel(); 355 panel5.setLayout(new FlowLayout()); 356 // Cancel 357 JButton cancel = new JButton(Bundle.getMessage("ButtonCancel")); // NOI18N 358 panel5.add(cancel); 359 cancel.addActionListener(this::cancelAddPressed); 360 cancel.setToolTipText(Bundle.getMessage("CancelLogixNGTableButtonHint")); // NOI18N 361 362 addLogixNGFrame.addWindowListener(new java.awt.event.WindowAdapter() { 363 @Override 364 public void windowClosing(java.awt.event.WindowEvent e) { 365 cancelAddPressed(null); 366 } 367 }); 368 contentPane.add(panel5); 369 370 _autoSystemName.addItemListener((ItemEvent e) -> { 371 autoSystemName(); 372 }); 373 return panel5; 374 } 375 376 @Override 377 protected void getListenerRefsIncludingChildren(NamedTable table, java.util.List<String> list) { 378 // Do nothing 379 } 380 381 @Override 382 protected boolean hasChildren(NamedTable table) { 383 // Tables doesn't have children 384 return false; 385 } 386 387 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogixNGTableTableAction.class); 388 389}