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