001package apps.startup; 002 003import jmri.util.startup.StartupModel; 004import apps.ConfigBundle; 005import jmri.util.startup.StartupActionsManager; 006import java.awt.Component; 007import java.awt.event.ActionEvent; 008import java.awt.event.ActionListener; 009import java.awt.event.MouseEvent; 010import java.beans.IndexedPropertyChangeEvent; 011import java.beans.PropertyChangeEvent; 012import java.beans.PropertyChangeListener; 013import java.util.ArrayList; 014import java.util.ResourceBundle; 015import javax.swing.GroupLayout; 016import javax.swing.JButton; 017import javax.swing.JComponent; 018import javax.swing.JLabel; 019import javax.swing.JMenuItem; 020import javax.swing.JPanel; 021import javax.swing.JPopupMenu; 022import javax.swing.JScrollPane; 023import javax.swing.JTable; 024import javax.swing.LayoutStyle; 025import javax.swing.ListSelectionModel; 026import javax.swing.event.ListSelectionEvent; 027import javax.swing.table.AbstractTableModel; 028import jmri.InstanceManager; 029import jmri.profile.Profile; 030import jmri.profile.ProfileManager; 031import jmri.swing.PreferencesPanel; 032import org.openide.util.lookup.ServiceProvider; 033 034/** 035 * Preferences panel to configure optional actions taken at startup. 036 * <p> 037 * This panel will only display (and save) preferences where 038 * {@link StartupModel#isValid()} is true. 039 * 040 * @author Randall Wood Copyright 2016, 2020 041 */ 042@ServiceProvider(service = PreferencesPanel.class) 043public class StartupActionsPreferencesPanel extends JPanel implements PreferencesPanel { 044 045 /** 046 * Creates new form StartupActionsPreferencesPanel 047 */ 048 public StartupActionsPreferencesPanel() { 049 initComponents(); 050 this.actionsTbl.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> { 051 int row = this.actionsTbl.getSelectedRow(); 052 this.upBtn.setEnabled(row != 0 && row != -1); 053 this.downBtn.setEnabled(row != this.actionsTbl.getRowCount() - 1 && row != -1); 054 this.removeBtn.setEnabled(row != -1); 055 }); 056 ArrayList<JMenuItem> items = new ArrayList<>(); 057 InstanceManager.getDefault(StartupActionsManager.class).getFactories().values().stream().forEach((factory) -> { 058 JMenuItem item = new JMenuItem(factory.getActionText()); 059 item.addActionListener((ActionEvent e) -> { 060 StartupModel model = factory.newModel(); 061 factory.editModel(model, this.getTopLevelAncestor()); 062 if (model.isValid()) { 063 InstanceManager.getDefault(StartupActionsManager.class).addAction(model); 064 } 065 }); 066 items.add(item); 067 }); 068 items.sort((JMenuItem o1, JMenuItem o2) -> o1.getText().compareTo(o2.getText())); 069 items.stream().forEach((item) -> { 070 this.actionsMenu.add(item); 071 }); 072 } 073 074 /** 075 * This method is called from within the constructor to performAction the 076 * form. WARNING: Do NOT modify this code. The content of this method is 077 * always regenerated by the Form Editor. 078 */ 079 // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents 080 private void initComponents() { 081 082 actionsMenu = new JPopupMenu(); 083 jScrollPane1 = new JScrollPane(); 084 actionsTbl = new JTable() { 085 086 //Implement table cell tool tips. 087 @Override 088 public String getToolTipText(MouseEvent e) { 089 try { 090 return getValueAt(rowAtPoint(e.getPoint()), -1).toString(); 091 } catch (RuntimeException e1) { 092 //catch null pointer exception if mouse is over an empty line 093 } 094 return null; 095 } 096 }; 097 addBtn = new JButton(); 098 removeBtn = new JButton(); 099 startupLbl = new JLabel(); 100 upBtn = new JButton(); 101 downBtn = new JButton(); 102 moveLbl = new JLabel(); 103 recommendationsLbl = new JLabel(); 104 105 actionsTbl.setDefaultRenderer(StartupModel.class, new StartupModelCellRenderer()); 106 actionsTbl.setDefaultEditor(StartupModel.class, new StartupModelCellEditor()); 107 actionsTbl.setModel(new TableModel(InstanceManager.getDefault(StartupActionsManager.class))); 108 actionsTbl.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 109 actionsTbl.getTableHeader().setReorderingAllowed(false); 110 jScrollPane1.setViewportView(actionsTbl); 111 actionsTbl.getColumnModel().getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 112 113 ResourceBundle bundle = ResourceBundle.getBundle("apps/startup/Bundle"); // NOI18N 114 addBtn.setText(bundle.getString("StartupActionsPreferencesPanel.addBtn.text")); // NOI18N 115 addBtn.addActionListener(new ActionListener() { 116 @Override 117 public void actionPerformed(ActionEvent evt) { 118 addBtnActionPerformed(evt); 119 } 120 }); 121 122 removeBtn.setText(bundle.getString("StartupActionsPreferencesPanel.removeBtn.text")); // NOI18N 123 removeBtn.setEnabled(false); 124 removeBtn.addActionListener(new ActionListener() { 125 @Override 126 public void actionPerformed(ActionEvent evt) { 127 removeBtnActionPerformed(evt); 128 } 129 }); 130 131 startupLbl.setText(bundle.getString("StartupActionsPreferencesPanel.startupLbl.text")); // NOI18N 132 133 upBtn.setText(bundle.getString("StartupActionsPreferencesPanel.upBtn.text")); // NOI18N 134 upBtn.setEnabled(false); 135 upBtn.addActionListener(new ActionListener() { 136 @Override 137 public void actionPerformed(ActionEvent evt) { 138 upBtnActionPerformed(evt); 139 } 140 }); 141 142 downBtn.setText(bundle.getString("StartupActionsPreferencesPanel.downBtn.text")); // NOI18N 143 downBtn.setEnabled(false); 144 downBtn.addActionListener(new ActionListener() { 145 @Override 146 public void actionPerformed(ActionEvent evt) { 147 downBtnActionPerformed(evt); 148 } 149 }); 150 151 moveLbl.setText(bundle.getString("StartupActionsPreferencesPanel.moveLbl.text")); // NOI18N 152 153 recommendationsLbl.setText(bundle.getString("StartupActionsPreferencesPanel.recommendationsLbl.text")); // NOI18N 154 155 GroupLayout layout = new GroupLayout(this); 156 this.setLayout(layout); 157 layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) 158 .addGroup(layout.createSequentialGroup() 159 .addContainerGap() 160 .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) 161 .addComponent(recommendationsLbl) 162 .addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 487, Short.MAX_VALUE) 163 .addGroup(layout.createSequentialGroup() 164 .addComponent(addBtn) 165 .addGap(18, 18, 18) 166 .addComponent(moveLbl) 167 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 168 .addComponent(upBtn) 169 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 170 .addComponent(downBtn) 171 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 172 .addComponent(removeBtn)) 173 .addComponent(startupLbl)) 174 .addContainerGap()) 175 ); 176 layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) 177 .addGroup(layout.createSequentialGroup() 178 .addContainerGap() 179 .addComponent(startupLbl, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 180 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 181 .addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 200, Short.MAX_VALUE) 182 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 183 .addComponent(recommendationsLbl, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 184 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 185 .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) 186 .addComponent(addBtn) 187 .addComponent(removeBtn) 188 .addComponent(upBtn) 189 .addComponent(downBtn) 190 .addComponent(moveLbl)) 191 .addContainerGap()) 192 ); 193 }// </editor-fold>//GEN-END:initComponents 194 195 private void addBtnActionPerformed(ActionEvent evt) {//GEN-FIRST:event_addBtnActionPerformed 196 Component c = (Component) evt.getSource(); 197 this.actionsMenu.show(c, 0 - 1, c.getHeight()); 198 }//GEN-LAST:event_addBtnActionPerformed 199 200 private void removeBtnActionPerformed(ActionEvent evt) {//GEN-FIRST:event_removeBtnActionPerformed 201 int row = this.actionsTbl.getSelectedRow(); 202 if (row != -1) { 203 StartupModel model = InstanceManager.getDefault(StartupActionsManager.class).getActions(row); 204 InstanceManager.getDefault(StartupActionsManager.class).removeAction(model); 205 } 206 }//GEN-LAST:event_removeBtnActionPerformed 207 208 private void upBtnActionPerformed(ActionEvent evt) {//GEN-FIRST:event_upBtnActionPerformed 209 int row = this.actionsTbl.getSelectedRow(); 210 if (row != 0) { 211 InstanceManager.getDefault(StartupActionsManager.class).moveAction(row, row - 1); 212 this.actionsTbl.setRowSelectionInterval(row - 1, row - 1); 213 } 214 }//GEN-LAST:event_upBtnActionPerformed 215 216 private void downBtnActionPerformed(ActionEvent evt) {//GEN-FIRST:event_downBtnActionPerformed 217 int row = this.actionsTbl.getSelectedRow(); 218 if (row != this.actionsTbl.getRowCount() - 1) { 219 InstanceManager.getDefault(StartupActionsManager.class).moveAction(row, row + 1); 220 this.actionsTbl.setRowSelectionInterval(row + 1, row + 1); 221 } 222 }//GEN-LAST:event_downBtnActionPerformed 223 224 @Override 225 public String getPreferencesItem() { 226 return "STARTUP"; // NOI18N 227 } 228 229 @Override 230 public String getPreferencesItemText() { 231 return ConfigBundle.getMessage("MenuStartUp"); // NOI18N 232 } 233 234 @Override 235 public String getTabbedPreferencesTitle() { 236 return null; 237 } 238 239 @Override 240 public String getLabelKey() { 241 return null; 242 } 243 244 @Override 245 public JComponent getPreferencesComponent() { 246 return this; 247 } 248 249 @Override 250 public boolean isPersistant() { 251 return true; 252 } 253 254 @Override 255 public String getPreferencesTooltip() { 256 return null; 257 } 258 259 @Override 260 public void savePreferences() { 261 Profile profile = ProfileManager.getDefault().getActiveProfile(); 262 InstanceManager.getDefault(StartupActionsManager.class).savePreferences(profile); 263 } 264 265 @Override 266 public boolean isDirty() { 267 return InstanceManager.getDefault(StartupActionsManager.class).isDirty(); 268 } 269 270 @Override 271 public boolean isRestartRequired() { 272 return InstanceManager.getDefault(StartupActionsManager.class).isRestartRequired(); 273 } 274 275 @Override 276 public boolean isPreferencesValid() { 277 // To really test would require that the models know their valid state 278 // they don't, and it can change externally, so we don't really check. 279 return true; 280 } 281 282 283 // Variables declaration - do not modify//GEN-BEGIN:variables 284 JPopupMenu actionsMenu; 285 JTable actionsTbl; 286 JButton addBtn; 287 JButton downBtn; 288 JScrollPane jScrollPane1; 289 JLabel moveLbl; 290 JLabel recommendationsLbl; 291 JButton removeBtn; 292 JLabel startupLbl; 293 JButton upBtn; 294 // End of variables declaration//GEN-END:variables 295 296 private static class TableModel extends AbstractTableModel implements PropertyChangeListener { 297 298 private final StartupActionsManager manager; 299 300 @SuppressWarnings("LeakingThisInConstructor") 301 public TableModel(StartupActionsManager manager) { 302 this.manager = manager; 303 this.manager.addPropertyChangeListener(this); 304 } 305 306 @Override 307 public int getRowCount() { 308 return this.manager.getActions().length; 309 } 310 311 @Override 312 public int getColumnCount() { 313 return 2; 314 } 315 316 @Override 317 public Object getValueAt(int rowIndex, int columnIndex) { 318 StartupModel model = this.manager.getActions(rowIndex); 319 switch (columnIndex) { 320 case -1: // tooltip 321 return model.toString(); 322 case 0: 323 return model; 324 case 1: 325 return this.manager.getFactories(model.getClass()).getDescription(); 326 default: 327 return null; 328 } 329 330 } 331 332 @Override 333 public String getColumnName(int columnIndex) { 334 switch (columnIndex) { 335 case 0: 336 return Bundle.getMessage("StartupActionsTableModel.name"); // NOI18N 337 case 1: 338 return Bundle.getMessage("StartupActionsTableModel.type"); // NOI18N 339 default: 340 return null; 341 } 342 } 343 344 @Override 345 public Class<?> getColumnClass(int columnIndex) { 346 switch (columnIndex) { 347 case 0: 348 return StartupModel.class; 349 case 1: 350 return String.class; 351 default: 352 return null; 353 } 354 } 355 356 @Override 357 public boolean isCellEditable(int rowIndex, int columnIndex) { 358 return columnIndex == 0; 359 } 360 361 @Override 362 public void propertyChange(PropertyChangeEvent evt) { 363 int index = -1; 364 if (evt instanceof IndexedPropertyChangeEvent) { 365 index = ((IndexedPropertyChangeEvent) evt).getIndex(); 366 } 367 if (index != -1 && evt.getOldValue() instanceof Integer) { 368 this.fireTableRowsUpdated((Integer) evt.getOldValue(), index); 369 } else { 370 this.fireTableDataChanged(); 371 } 372 } 373 } 374}