001/*============================================================================* 002 * WARNING This class contains automatically modified code. WARNING * 003 * * 004 * The method initComponents() and the variable declarations between the * 005 * "// Variables declaration - do not modify" and * 006 * "// End of variables declaration" comments will be overwritten if modified * 007 * by hand. Using the NetBeans IDE to edit this file is strongly recommended. * 008 * * 009 * See http://jmri.org/help/en/html/doc/Technical/NetBeansGUIEditor.shtml for * 010 * more information. * 011 *============================================================================*/ 012package jmri.profile; 013 014import java.awt.Cursor; 015import java.awt.Frame; 016import java.awt.event.ActionEvent; 017import java.awt.event.ActionListener; 018import java.awt.event.MouseEvent; 019import java.beans.PropertyChangeListener; 020import java.io.File; 021import java.io.IOException; 022import java.util.ArrayList; 023import java.util.ResourceBundle; 024 025import javax.swing.ButtonGroup; 026import javax.swing.GroupLayout; 027import javax.swing.JButton; 028import javax.swing.JComponent; 029import javax.swing.JFileChooser; 030import javax.swing.JLabel; 031import javax.swing.JMenuItem; 032import javax.swing.JPanel; 033import javax.swing.JPopupMenu; 034import javax.swing.JRadioButton; 035import javax.swing.JScrollPane; 036import javax.swing.JSpinner; 037import javax.swing.JTabbedPane; 038import javax.swing.JTable; 039import javax.swing.LayoutStyle; 040import javax.swing.SpinnerNumberModel; 041import javax.swing.SwingUtilities; 042import javax.swing.event.ChangeEvent; 043import javax.swing.event.ChangeListener; 044import javax.swing.event.ListSelectionEvent; 045import javax.swing.event.ListSelectionListener; 046import javax.swing.event.PopupMenuEvent; 047import javax.swing.event.PopupMenuListener; 048import javax.swing.filechooser.FileNameExtensionFilter; 049 050import jmri.jmrit.roster.Roster; 051import jmri.swing.PreferencesPanel; 052import jmri.util.FileUtil; 053import jmri.util.prefs.InitializationException; 054import jmri.util.swing.JmriJOptionPane; 055 056import org.jdom2.JDOMException; 057import org.openide.util.lookup.ServiceProvider; 058 059/** 060 * A JPanel suitable for managing {@link jmri.profile.Profile}s within a 061 * preferences window. 062 * 063 * @author Randall Wood 064 */ 065@ServiceProvider(service = PreferencesPanel.class) 066public final class ProfilePreferencesPanel extends JPanel implements PreferencesPanel { 067 068 /** 069 * Creates new form ProfilePreferencesPanel 070 */ 071 public ProfilePreferencesPanel() { 072 initComponents(); 073 this.spinnerTimeout.setValue(ProfileManager.getDefault().getAutoStartActiveProfileTimeout()); 074 this.profilesTblValueChanged(null); 075 this.searchPathsTblValueChanged(null); 076 int index = ProfileManager.getDefault().getAllProfiles().indexOf(ProfileManager.getDefault().getActiveProfile()); 077 if (index != -1) { 078 this.profilesTbl.setRowSelectionInterval(index, index); 079 } 080 } 081 082 /** 083 * This method is called from within the constructor to initialize the form. 084 * WARNING: Do NOT modify this code. The content of this method is always 085 * regenerated by the Form Editor. 086 */ 087 // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents 088 private void initComponents() { 089 090 profilesPopupMenu = new JPopupMenu(); 091 renameMI = new JMenuItem(); 092 jSeparator1 = new JPopupMenu.Separator(); 093 copyMI = new JMenuItem(); 094 deleteMI = new JMenuItem(); 095 grpStartWithSelectors = new ButtonGroup(); 096 jTabbedPane1 = new JTabbedPane(); 097 enabledPanel = new JPanel(); 098 jScrollPane1 = new JScrollPane(); 099 profilesTbl = new JTable() { 100 //Implement table cell tool tips. 101 @Override 102 public String getToolTipText(MouseEvent e) { 103 try { 104 return getValueAt(rowAtPoint(e.getPoint()), -1).toString(); 105 } catch (RuntimeException e1) { 106 //catch null pointer exception if mouse is over an empty line 107 } 108 return null; 109 }}; 110 btnOpenExistingProfile = new JButton(); 111 btnDeleteProfile = new JButton(); 112 btnCreateNewProfile = new JButton(); 113 btnActivateProfile = new JButton(); 114 btnExportProfile = new JButton(); 115 btnCopyProfile = new JButton(); 116 spinnerTimeout = new JSpinner(); 117 jLabel1 = new JLabel(); 118 rdoStartWithActiveProfile = new JRadioButton(); 119 rdoStartWithProfileSelector = new JRadioButton(); 120 searchPathsPanel = new JPanel(); 121 btnRemoveSearchPath = new JButton(); 122 btnAddSearchPath = new JButton(); 123 jScrollPane3 = new JScrollPane(); 124 searchPathsTbl = new JTable() { 125 //Implement table cell tool tips. 126 @Override 127 public String getToolTipText(MouseEvent e) { 128 try { 129 return getValueAt(rowAtPoint(e.getPoint()), -1).toString(); 130 } catch (RuntimeException e1) { 131 //catch null pointer exception if mouse is over an empty line 132 } 133 return null; 134 }}; 135 136 profilesPopupMenu.addPopupMenuListener(new PopupMenuListener() { 137 @Override 138 public void popupMenuWillBecomeVisible(PopupMenuEvent evt) { 139 profilesPopupMenuPopupMenuWillBecomeVisible(evt); 140 } 141 @Override 142 public void popupMenuWillBecomeInvisible(PopupMenuEvent evt) { 143 } 144 @Override 145 public void popupMenuCanceled(PopupMenuEvent evt) { 146 } 147 }); 148 149 ResourceBundle bundle = ResourceBundle.getBundle("jmri/profile/Bundle"); // NOI18N 150 renameMI.setText(bundle.getString("ProfilePreferencesPanel.renameMI.text")); // NOI18N 151 renameMI.addActionListener(new ActionListener() { 152 @Override 153 public void actionPerformed(ActionEvent evt) { 154 renameMIActionPerformed(evt); 155 } 156 }); 157 profilesPopupMenu.add(renameMI); 158 profilesPopupMenu.add(jSeparator1); 159 160 copyMI.setText(bundle.getString("ProfilePreferencesPanel.copyMI.text")); // NOI18N 161 profilesPopupMenu.add(copyMI); 162 163 deleteMI.setText(bundle.getString("ProfilePreferencesPanel.deleteMI.text")); // NOI18N 164 profilesPopupMenu.add(deleteMI); 165 166 if (ProfileManager.getDefault().isAutoStartActiveProfile()) { 167 this.rdoStartWithActiveProfile.setSelected(true); 168 } else { 169 this.rdoStartWithProfileSelector.setSelected(true); 170 } 171 172 profilesTbl.setModel(new ProfileTableModel()); 173 profilesTbl.getSelectionModel().addListSelectionListener(new ProfilesSelectionListener()); 174 profilesTbl.getTableHeader().setReorderingAllowed(false); 175 jScrollPane1.setViewportView(profilesTbl); 176 177 btnOpenExistingProfile.setText(bundle.getString("ProfilePreferencesPanel.btnOpenExistingProfile.text")); // NOI18N 178 btnOpenExistingProfile.setToolTipText(bundle.getString("ProfilePreferencesPanel.btnOpenExistingProfile.toolTipText")); // NOI18N 179 btnOpenExistingProfile.addActionListener(new ActionListener() { 180 @Override 181 public void actionPerformed(ActionEvent evt) { 182 btnOpenExistingProfileActionPerformed(evt); 183 } 184 }); 185 186 btnDeleteProfile.setText(bundle.getString("ProfilePreferencesPanel.btnDeleteProfile.text")); // NOI18N 187 btnDeleteProfile.setToolTipText(bundle.getString("ProfilePreferencesPanel.btnDeleteProfile.toolTipText")); // NOI18N 188 btnDeleteProfile.addActionListener(new ActionListener() { 189 @Override 190 public void actionPerformed(ActionEvent evt) { 191 btnDeleteProfileActionPerformed(evt); 192 } 193 }); 194 195 btnCreateNewProfile.setText(bundle.getString("ProfilePreferencesPanel.btnCreateNewProfile.text")); // NOI18N 196 btnCreateNewProfile.setToolTipText(bundle.getString("ProfilePreferencesPanel.btnCreateNewProfile.toolTipText")); // NOI18N 197 btnCreateNewProfile.addActionListener(new ActionListener() { 198 @Override 199 public void actionPerformed(ActionEvent evt) { 200 btnCreateNewProfileActionPerformed(evt); 201 } 202 }); 203 204 btnActivateProfile.setText(bundle.getString("ProfilePreferencesPanel.btnActivateProfile.text")); // NOI18N 205 btnActivateProfile.setToolTipText(bundle.getString("ProfilePreferencesPanel.btnActivateProfile.toolTipText")); // NOI18N 206 btnActivateProfile.addActionListener(new ActionListener() { 207 @Override 208 public void actionPerformed(ActionEvent evt) { 209 btnActivateProfileActionPerformed(evt); 210 } 211 }); 212 213 btnExportProfile.setText(bundle.getString("ProfilePreferencesPanel.btnExportProfile.text")); // NOI18N 214 btnExportProfile.setToolTipText(bundle.getString("ProfilePreferencesPanel.btnExportProfile.toolTipText")); // NOI18N 215 btnExportProfile.addActionListener(new ActionListener() { 216 @Override 217 public void actionPerformed(ActionEvent evt) { 218 btnExportProfileActionPerformed(evt); 219 } 220 }); 221 222 btnCopyProfile.setText(bundle.getString("ProfilePreferencesPanel.btnCopyProfile.text")); // NOI18N 223 btnCopyProfile.addActionListener(new ActionListener() { 224 @Override 225 public void actionPerformed(ActionEvent evt) { 226 btnCopyProfileActionPerformed(evt); 227 } 228 }); 229 230 spinnerTimeout.setModel(new SpinnerNumberModel(10, 0, 500, 1)); 231 spinnerTimeout.addChangeListener(new ChangeListener() { 232 @Override 233 public void stateChanged(ChangeEvent evt) { 234 spinnerTimeoutStateChanged(evt); 235 } 236 }); 237 238 jLabel1.setText(bundle.getString("ProfilePreferencesPanel.jLabel1.text")); // NOI18N 239 240 grpStartWithSelectors.add(rdoStartWithActiveProfile); 241 rdoStartWithActiveProfile.setText(bundle.getString("ProfilePreferencesPanel.rdoStartWithActiveProfile.text")); // NOI18N 242 rdoStartWithActiveProfile.addActionListener(new ActionListener() { 243 @Override 244 public void actionPerformed(ActionEvent evt) { 245 rdoStartWithActiveProfileActionPerformed(evt); 246 } 247 }); 248 249 grpStartWithSelectors.add(rdoStartWithProfileSelector); 250 rdoStartWithProfileSelector.setText(bundle.getString("ProfilePreferencesPanel.rdoStartWithProfileSelector.text")); // NOI18N 251 rdoStartWithProfileSelector.addActionListener(new ActionListener() { 252 @Override 253 public void actionPerformed(ActionEvent evt) { 254 rdoStartWithProfileSelectorActionPerformed(evt); 255 } 256 }); 257 258 GroupLayout enabledPanelLayout = new GroupLayout(enabledPanel); 259 enabledPanel.setLayout(enabledPanelLayout); 260 enabledPanelLayout.setHorizontalGroup(enabledPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) 261 .addGroup(enabledPanelLayout.createSequentialGroup() 262 .addContainerGap() 263 .addGroup(enabledPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) 264 .addComponent(jScrollPane1) 265 .addGroup(enabledPanelLayout.createSequentialGroup() 266 .addComponent(btnActivateProfile) 267 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 268 .addComponent(btnOpenExistingProfile) 269 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 270 .addComponent(btnCreateNewProfile) 271 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 272 .addComponent(btnCopyProfile) 273 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 274 .addComponent(btnExportProfile) 275 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 60, Short.MAX_VALUE) 276 .addComponent(btnDeleteProfile)) 277 .addGroup(enabledPanelLayout.createSequentialGroup() 278 .addGroup(enabledPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) 279 .addComponent(rdoStartWithActiveProfile) 280 .addGroup(enabledPanelLayout.createSequentialGroup() 281 .addComponent(rdoStartWithProfileSelector) 282 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 283 .addComponent(spinnerTimeout, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 284 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 285 .addComponent(jLabel1))) 286 .addGap(0, 0, Short.MAX_VALUE))) 287 .addContainerGap()) 288 ); 289 enabledPanelLayout.setVerticalGroup(enabledPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) 290 .addGroup(enabledPanelLayout.createSequentialGroup() 291 .addContainerGap() 292 .addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 190, Short.MAX_VALUE) 293 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 294 .addGroup(enabledPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) 295 .addComponent(btnOpenExistingProfile) 296 .addComponent(btnCreateNewProfile) 297 .addComponent(btnActivateProfile) 298 .addComponent(btnExportProfile) 299 .addComponent(btnDeleteProfile) 300 .addComponent(btnCopyProfile)) 301 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 302 .addComponent(rdoStartWithActiveProfile) 303 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 304 .addGroup(enabledPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) 305 .addComponent(rdoStartWithProfileSelector) 306 .addComponent(spinnerTimeout, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 307 .addComponent(jLabel1))) 308 ); 309 310 jTabbedPane1.addTab(bundle.getString("ProfilePreferencesPanel.enabledPanel.TabConstraints.tabTitle"), enabledPanel); // NOI18N 311 312 btnRemoveSearchPath.setText(bundle.getString("ProfilePreferencesPanel.btnRemoveSearchPath.text")); // NOI18N 313 btnRemoveSearchPath.setToolTipText(bundle.getString("ProfilePreferencesPanel.btnRemoveSearchPath.toolTipText")); // NOI18N 314 btnRemoveSearchPath.addActionListener(new ActionListener() { 315 @Override 316 public void actionPerformed(ActionEvent evt) { 317 btnRemoveSearchPathActionPerformed(evt); 318 } 319 }); 320 321 btnAddSearchPath.setText(bundle.getString("ProfilePreferencesPanel.btnAddSearchPath.text")); // NOI18N 322 btnAddSearchPath.setToolTipText(bundle.getString("ProfilePreferencesPanel.btnAddSearchPath.toolTipText")); // NOI18N 323 btnAddSearchPath.addActionListener(new ActionListener() { 324 @Override 325 public void actionPerformed(ActionEvent evt) { 326 btnAddSearchPathActionPerformed(evt); 327 } 328 }); 329 330 searchPathsTbl.setModel(new SearchPathTableModel()); 331 searchPathsTbl.getSelectionModel().addListSelectionListener(new SearchPathSelectionListener()); 332 searchPathsTbl.getTableHeader().setReorderingAllowed(false); 333 jScrollPane3.setViewportView(searchPathsTbl); 334 335 GroupLayout searchPathsPanelLayout = new GroupLayout(searchPathsPanel); 336 searchPathsPanel.setLayout(searchPathsPanelLayout); 337 searchPathsPanelLayout.setHorizontalGroup(searchPathsPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) 338 .addGroup(searchPathsPanelLayout.createSequentialGroup() 339 .addContainerGap() 340 .addGroup(searchPathsPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) 341 .addComponent(jScrollPane3, GroupLayout.DEFAULT_SIZE, 667, Short.MAX_VALUE) 342 .addGroup(searchPathsPanelLayout.createSequentialGroup() 343 .addComponent(btnAddSearchPath) 344 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 345 .addComponent(btnRemoveSearchPath))) 346 .addContainerGap()) 347 ); 348 searchPathsPanelLayout.setVerticalGroup(searchPathsPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) 349 .addGroup(searchPathsPanelLayout.createSequentialGroup() 350 .addContainerGap() 351 .addComponent(jScrollPane3, GroupLayout.DEFAULT_SIZE, 247, Short.MAX_VALUE) 352 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 353 .addGroup(searchPathsPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) 354 .addComponent(btnAddSearchPath) 355 .addComponent(btnRemoveSearchPath)) 356 .addContainerGap()) 357 ); 358 359 jTabbedPane1.addTab(bundle.getString("ProfilePreferencesPanel.searchPathsPanel.TabConstraints.tabTitle_1"), searchPathsPanel); // NOI18N 360 361 GroupLayout layout = new GroupLayout(this); 362 this.setLayout(layout); 363 layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) 364 .addComponent(jTabbedPane1, GroupLayout.Alignment.TRAILING) 365 ); 366 layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) 367 .addComponent(jTabbedPane1) 368 ); 369 370 jTabbedPane1.getAccessibleContext().setAccessibleName(bundle.getString("ProfilePreferencesPanel.enabledPanel.TabConstraints.tabTitle")); // NOI18N 371 }// </editor-fold>//GEN-END:initComponents 372 373 private void renameMIActionPerformed(ActionEvent evt) {//GEN-FIRST:event_renameMIActionPerformed 374 // TODO add your handling code here: 375 }//GEN-LAST:event_renameMIActionPerformed 376 377 private void profilesPopupMenuPopupMenuWillBecomeVisible(PopupMenuEvent evt) {//GEN-FIRST:event_profilesPopupMenuPopupMenuWillBecomeVisible 378 if (profilesTbl.getSelectedRowCount() == 1) { 379 this.renameMI.setEnabled(true); 380 } 381 }//GEN-LAST:event_profilesPopupMenuPopupMenuWillBecomeVisible 382 383 private void btnAddSearchPathActionPerformed(ActionEvent evt) {//GEN-FIRST:event_btnAddSearchPathActionPerformed 384 JFileChooser chooser = new jmri.util.swing.JmriJFileChooser(FileUtil.getHomePath()); 385 chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); 386 chooser.setFileFilter(new ProfileFileFilter()); 387 chooser.setFileView(new ProfileFileView()); 388 // TODO: Use NetBeans OpenDialog if its availble 389 if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { 390 try { 391 ProfileManager.getDefault().addSearchPath(chooser.getSelectedFile()); 392 int index = ProfileManager.getDefault().getAllSearchPaths().indexOf(chooser.getSelectedFile()); 393 this.searchPathsTbl.setRowSelectionInterval(index, index); 394 } catch (IOException ex) { 395 log.warn("Unable to write profiles while adding search path {}", chooser.getSelectedFile().getPath(), ex); 396 JmriJOptionPane.showMessageDialog(this, 397 Bundle.getMessage("ProfilePreferencesPanel.btnAddSearchPath.errorMessage", 398 chooser.getSelectedFile().getPath(), 399 ex.getLocalizedMessage()), 400 Bundle.getMessage("ProfilePreferencesPanel.btnAddSearchPath.errorTitle"), 401 JmriJOptionPane.ERROR_MESSAGE); 402 } 403 } 404 }//GEN-LAST:event_btnAddSearchPathActionPerformed 405 406 private void btnRemoveSearchPathActionPerformed(ActionEvent evt) {//GEN-FIRST:event_btnRemoveSearchPathActionPerformed 407 ArrayList<File> paths = new ArrayList<>(this.searchPathsTbl.getSelectedRowCount()); 408 for (int row : this.searchPathsTbl.getSelectedRows()) { 409 paths.add(ProfileManager.getDefault().getSearchPaths(row)); 410 } 411 for (File path : paths) { 412 try { 413 ProfileManager.getDefault().removeSearchPath(path); 414 } catch (IOException ex) { 415 log.warn("Unable to write profiles while removing search path {}", path.getPath(), ex); 416 JmriJOptionPane.showMessageDialog(this, 417 Bundle.getMessage("ProfilePreferencesPanel.btnRemoveSearchPath.errorMessage", path.getPath(), ex.getLocalizedMessage()), 418 Bundle.getMessage("ProfilePreferencesPanel.btnRemoveSearchPath.errorTitle"), 419 JmriJOptionPane.ERROR_MESSAGE); 420 } 421 } 422 }//GEN-LAST:event_btnRemoveSearchPathActionPerformed 423 424 private void btnExportProfileActionPerformed(ActionEvent evt) {//GEN-FIRST:event_btnExportProfileActionPerformed 425 Profile p = ProfileManager.getDefault().getProfiles(profilesTbl.getSelectedRow()); 426 if (p == null) { 427 // abort if selection does not match an existing profile 428 return; 429 } 430 JFileChooser chooser = new jmri.util.swing.JmriJFileChooser(); 431 chooser.setFileFilter(new FileNameExtensionFilter("ZIP Archives", "zip")); 432 chooser.setFileView(new ProfileFileView()); 433 chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); 434 chooser.setSelectedFile(new File(p.getName() + ".zip")); 435 if (chooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) { 436 try { 437 if (chooser.getSelectedFile().exists()) { 438 int result = JmriJOptionPane.showConfirmDialog(this, 439 Bundle.getMessage("ProfilePreferencesPanel.btnExportProfile.overwriteMessage", 440 chooser.getSelectedFile().getName(), 441 chooser.getSelectedFile().getParentFile().getName()), 442 Bundle.getMessage("ProfilePreferencesPanel.btnExportProfile.overwriteTitle"), 443 JmriJOptionPane.YES_NO_OPTION, 444 JmriJOptionPane.WARNING_MESSAGE); 445 if (result == JmriJOptionPane.YES_OPTION) { 446 if (!chooser.getSelectedFile().delete()) { 447 JmriJOptionPane.showMessageDialog(this, 448 Bundle.getMessage("ProfilePreferencesPanel.btnExportProfile.failureToDeleteMessage", 449 chooser.getSelectedFile().getName(), 450 chooser.getSelectedFile().getParentFile().getName()), 451 Bundle.getMessage("ProfilePreferencesPanel.btnExportProfile.failureToDeleteTitle"), 452 JmriJOptionPane.ERROR_MESSAGE); 453 } 454 } else { 455 this.btnExportProfileActionPerformed(evt); 456 return; 457 } 458 } 459 boolean exportExternalUserFiles = false; 460 boolean exportExternalRoster = false; 461 if (!(new File(FileUtil.getUserFilesPath())).getCanonicalPath().startsWith(p.getPath().getCanonicalPath())) { 462 int result = JmriJOptionPane.showConfirmDialog(this, 463 Bundle.getMessage("ProfilePreferencesPanel.btnExportProfile.externalUserFilesMessage"), 464 Bundle.getMessage("ProfilePreferencesPanel.btnExportProfile.externalUserFilesTitle"), 465 JmriJOptionPane.YES_NO_OPTION, 466 JmriJOptionPane.QUESTION_MESSAGE); 467 if (result == JmriJOptionPane.YES_OPTION) { 468 exportExternalUserFiles = true; 469 } 470 } 471 if (!(new File(Roster.getDefault().getRosterLocation())).getCanonicalPath().startsWith(p.getPath().getCanonicalPath()) 472 && !Roster.getDefault().getRosterLocation().startsWith(FileUtil.getUserFilesPath())) { 473 int result = JmriJOptionPane.showConfirmDialog(this, 474 Bundle.getMessage("ProfilePreferencesPanel.btnExportProfile.externalRosterMessage"), 475 Bundle.getMessage("ProfilePreferencesPanel.btnExportProfile.externalRosterTitle"), 476 JmriJOptionPane.YES_NO_OPTION, 477 JmriJOptionPane.QUESTION_MESSAGE); 478 if (result == JmriJOptionPane.YES_OPTION) { 479 exportExternalRoster = true; 480 } 481 } 482 //if (ProfileManager.getDefault().getActiveProfile() == p) { 483 // // TODO: save roster, panels, operations if needed and safe to do so 484 //} 485 ProfileManager.getDefault().export(p, chooser.getSelectedFile(), exportExternalUserFiles, exportExternalRoster); 486 log.info("Profile \"{}\" exported to \"{}\"", p.getName(), chooser.getSelectedFile().getName()); 487 JmriJOptionPane.showMessageDialog(this, 488 Bundle.getMessage("ProfilePreferencesPanel.btnExportProfile.successMessage", 489 p.getName(), chooser.getSelectedFile().getName()), 490 Bundle.getMessage("ProfilePreferencesPanel.btnExportProfile.successTitle"), 491 JmriJOptionPane.INFORMATION_MESSAGE); 492 } catch (IOException | JDOMException | InitializationException ex) { 493 log.warn("Unable to export profile \"{}\" to {}", p.getName(), chooser.getSelectedFile().getPath(), ex); 494 JmriJOptionPane.showMessageDialog(this, 495 Bundle.getMessage("ProfilePreferencesPanel.btnExportProfile.errorMessage", 496 p.getName(), 497 chooser.getSelectedFile().getPath(), 498 ex.getLocalizedMessage()), 499 Bundle.getMessage("ProfilePreferencesPanel.btnExportProfile.errorTitle"), 500 JmriJOptionPane.ERROR_MESSAGE); 501 } 502 } 503 }//GEN-LAST:event_btnExportProfileActionPerformed 504 505 private void btnActivateProfileActionPerformed(ActionEvent evt) {//GEN-FIRST:event_btnActivateProfileActionPerformed 506 try { 507 Profile p = ProfileManager.getDefault().getProfiles(profilesTbl.getSelectedRow()); 508 ProfileManager.getDefault().setNextActiveProfile(p); 509 ProfileManager.getDefault().saveActiveProfile(p, ProfileManager.getDefault().isAutoStartActiveProfile()); 510 } catch (IOException ex) { 511 log.error("Unable to save profile preferences", ex); 512 JmriJOptionPane.showMessageDialog(this, "Usable to save profile preferences.\n" + ex.getLocalizedMessage(), "Error", JmriJOptionPane.ERROR_MESSAGE); 513 } 514 }//GEN-LAST:event_btnActivateProfileActionPerformed 515 516 private void btnCreateNewProfileActionPerformed(ActionEvent evt) {//GEN-FIRST:event_btnCreateNewProfileActionPerformed 517 AddProfileDialog apd = new AddProfileDialog((Frame) SwingUtilities.getWindowAncestor(this), true, true); 518 apd.setLocationRelativeTo(this); 519 apd.setVisible(true); 520 }//GEN-LAST:event_btnCreateNewProfileActionPerformed 521 522 private void btnDeleteProfileActionPerformed(ActionEvent evt) {//GEN-FIRST:event_btnDeleteProfileActionPerformed 523 ArrayList<Profile> profiles = new ArrayList<>(this.profilesTbl.getSelectedRowCount()); 524 for (int row : this.profilesTbl.getSelectedRows()) { 525 profiles.add(ProfileManager.getDefault().getAllProfiles().get(row)); 526 } 527 for (Profile deletedProfile : profiles) { 528 if (!Profile.isProfile(deletedProfile.getPath())) { 529 int result = JmriJOptionPane.showOptionDialog(this, 530 Bundle.getMessage("ProfilePreferencesPanel.btnDeleteProfile.dlgMessageNotAProfile", deletedProfile.getName(),deletedProfile.getPath()), // NOI18N 531 Bundle.getMessage("ProfilePreferencesPanel.btnDeleteProfile.dlgTitle", deletedProfile.getName()), // NOI18N 532 JmriJOptionPane.OK_CANCEL_OPTION, 533 JmriJOptionPane.QUESTION_MESSAGE, 534 null, // use default icon 535 new String[]{ 536 Bundle.getMessage("ProfilePreferencesPanel.btnDeleteProfile.text"), // NOI18N 537 Bundle.getMessage("AddProfileDialog.btnCancel.text") // NOI18N 538 }, 539 JmriJOptionPane.CANCEL_OPTION 540 ); 541 if (result == JmriJOptionPane.OK_OPTION) { 542 ProfileManager.getDefault().removeProfile(deletedProfile); 543 log.info("Removed profile \"{}\" left directory {}", deletedProfile.getName(), deletedProfile.getPath()); 544 this.setCursor(Cursor.getDefaultCursor()); 545 profilesTbl.repaint(); 546 } 547 } else { 548 int result = JmriJOptionPane.showOptionDialog(this, 549 Bundle.getMessage("ProfilePreferencesPanel.btnDeleteProfile.dlgMessage", deletedProfile.getName(), deletedProfile.getPath(), "AAAAA" ), // NOI18N 550 Bundle.getMessage("ProfilePreferencesPanel.btnDeleteProfile.dlgTitle", deletedProfile.getName()), // NOI18N 551 JmriJOptionPane.OK_CANCEL_OPTION, 552 JmriJOptionPane.QUESTION_MESSAGE, 553 null, // use default icon 554 new String[]{ 555 Bundle.getMessage("ProfilePreferencesPanel.btnDeleteProfile.text"), // NOI18N 556 Bundle.getMessage("AddProfileDialog.btnCancel.text") // NOI18N 557 }, 558 JmriJOptionPane.CANCEL_OPTION 559 ); 560 if (result == JmriJOptionPane.OK_OPTION) { 561 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 562 if (!FileUtil.delete(deletedProfile.getPath())) { 563 log.warn("Unable to delete profile directory {}", deletedProfile.getPath()); 564 this.setCursor(Cursor.getDefaultCursor()); 565 JmriJOptionPane.showMessageDialog(this, 566 Bundle.getMessage("ProfilePreferencesPanel.btnDeleteProfile.errorMessage", deletedProfile.getPath()), 567 Bundle.getMessage("ProfilePreferencesPanel.btnDeleteProfile.errorMessage"), 568 JmriJOptionPane.ERROR_MESSAGE); 569 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 570 } 571 ProfileManager.getDefault().removeProfile(deletedProfile); 572 log.info("Removed profile \"{}\" from {}", deletedProfile.getName(), deletedProfile.getPath()); 573 } 574 } 575 this.setCursor(Cursor.getDefaultCursor()); 576 profilesTbl.repaint(); 577 } 578 }//GEN-LAST:event_btnDeleteProfileActionPerformed 579 580 private void btnOpenExistingProfileActionPerformed(ActionEvent evt) {//GEN-FIRST:event_btnOpenExistingProfileActionPerformed 581 JFileChooser chooser = new jmri.util.swing.JmriJFileChooser(FileUtil.getHomePath()); 582 chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); 583 chooser.setFileFilter(new ProfileFileFilter()); 584 chooser.setFileView(new ProfileFileView()); 585 // TODO: Use NetBeans OpenDialog if its availble 586 if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { 587 try { 588 if (!Profile.isProfile(chooser.getSelectedFile())) { 589 log.warn("{} is not a profile directory", chooser.getSelectedFile()); 590 JmriJOptionPane.showMessageDialog(this, 591 Bundle.getMessage("addExistingNotAProfile",chooser.getSelectedFile()), 592 Bundle.getMessage("addExistingNotAProfile"), 593 JmriJOptionPane.ERROR_MESSAGE); 594 return; 595 } else { 596 Profile p = new Profile(chooser.getSelectedFile()); 597 ProfileManager.getDefault().addProfile(p); 598 int index = ProfileManager.getDefault().getAllProfiles().indexOf(p); 599 profilesTbl.setRowSelectionInterval(index, index); 600 } 601 } catch (IOException ex) { 602 // new Profile can throw an exception, but not for directory is not a profile directory 603 // it just creates and invalid null profile 604 // which when subsequently deleted removes the entire directory tree. 605 log.warn("Unexpected error in new Profile({})", chooser.getSelectedFile(),ex); 606 return; 607 } 608 } 609 }//GEN-LAST:event_btnOpenExistingProfileActionPerformed 610 611 private void btnCopyProfileActionPerformed(ActionEvent evt) {//GEN-FIRST:event_btnCopyProfileActionPerformed 612 AddProfileDialog apd = new AddProfileDialog((Frame) SwingUtilities.getWindowAncestor(this), true, true); 613 apd.setSourceProfile(ProfileManager.getDefault().getAllProfiles().get(profilesTbl.getSelectedRow())); 614 apd.setLocationRelativeTo(this); 615 apd.setVisible(true); 616 }//GEN-LAST:event_btnCopyProfileActionPerformed 617 618 private void spinnerTimeoutStateChanged(ChangeEvent evt) {//GEN-FIRST:event_spinnerTimeoutStateChanged 619 ProfileManager.getDefault().setAutoStartActiveProfileTimeout((Integer) this.spinnerTimeout.getValue()); 620 try { 621 ProfileManager.getDefault().saveActiveProfile(); 622 } catch (IOException ex) { 623 log.error("Unable to save active profile.", ex); 624 } 625 }//GEN-LAST:event_spinnerTimeoutStateChanged 626 627 private void rdoStartWithActiveProfileActionPerformed(ActionEvent evt) {//GEN-FIRST:event_rdoStartWithActiveProfileActionPerformed 628 this.setAutoStartActiveProfile(true); 629 this.spinnerTimeout.setEnabled(false); 630 }//GEN-LAST:event_rdoStartWithActiveProfileActionPerformed 631 632 private void rdoStartWithProfileSelectorActionPerformed(ActionEvent evt) {//GEN-FIRST:event_rdoStartWithProfileSelectorActionPerformed 633 this.setAutoStartActiveProfile(false); 634 this.spinnerTimeout.setEnabled(true); 635 }//GEN-LAST:event_rdoStartWithProfileSelectorActionPerformed 636 637 private void setAutoStartActiveProfile(boolean automatic) { 638 ProfileManager.getDefault().setAutoStartActiveProfile(automatic); 639 try { 640 ProfileManager.getDefault().saveActiveProfile(); 641 } catch (IOException ex) { 642 log.error("Unable to save active profile.", ex); 643 } 644 } 645 646 // Variables declaration - do not modify//GEN-BEGIN:variables 647 private JButton btnActivateProfile; 648 private JButton btnAddSearchPath; 649 private JButton btnCopyProfile; 650 private JButton btnCreateNewProfile; 651 private JButton btnDeleteProfile; 652 private JButton btnExportProfile; 653 private JButton btnOpenExistingProfile; 654 private JButton btnRemoveSearchPath; 655 private JMenuItem copyMI; 656 private JMenuItem deleteMI; 657 private JPanel enabledPanel; 658 private ButtonGroup grpStartWithSelectors; 659 private JLabel jLabel1; 660 private JScrollPane jScrollPane1; 661 private JScrollPane jScrollPane3; 662 private JPopupMenu.Separator jSeparator1; 663 private JTabbedPane jTabbedPane1; 664 private JPopupMenu profilesPopupMenu; 665 private JTable profilesTbl; 666 private JRadioButton rdoStartWithActiveProfile; 667 private JRadioButton rdoStartWithProfileSelector; 668 private JMenuItem renameMI; 669 private JPanel searchPathsPanel; 670 private JTable searchPathsTbl; 671 private JSpinner spinnerTimeout; 672 // End of variables declaration//GEN-END:variables 673 674 @Override 675 public String getPreferencesItem() { 676 return "Profiles"; // NOI18N 677 } 678 679 @Override 680 public String getPreferencesItemText() { 681 return Bundle.getMessage("ProfilePreferencesPanel.enabledPanel.TabConstraints.tabTitle"); 682 } 683 684 @Override 685 public String getTabbedPreferencesTitle() { 686 return null; 687 } 688 689 @Override 690 public String getLabelKey() { 691 return null; 692 } 693 694 @Override 695 public JComponent getPreferencesComponent() { 696 return this; 697 } 698 699 @Override 700 public boolean isPersistant() { 701 return false; 702 } 703 704 @Override 705 public String getPreferencesTooltip() { 706 return null; 707 } 708 709 @Override 710 public void savePreferences() { 711 // Nothing to do since ProfileManager preferences are saved immediately 712 } 713 714 @Override 715 public int getSortOrder() { 716 return 1000; 717 } 718 719 public void dispose() { 720 ProfileManager.getDefault().removePropertyChangeListener((PropertyChangeListener) profilesTbl.getModel()); 721 } 722 723 private void profilesTblValueChanged(ListSelectionEvent e) { 724 if (profilesTbl.getSelectedRowCount() == 1 && profilesTbl.getSelectedRow() < ProfileManager.getDefault().getAllProfiles().size()) { 725 Profile p = ProfileManager.getDefault().getAllProfiles().get(profilesTbl.getSelectedRow()); 726 this.btnDeleteProfile.setEnabled(!p.equals(ProfileManager.getDefault().getActiveProfile())); 727 if (ProfileManager.getDefault().getNextActiveProfile() != null) { 728 this.btnActivateProfile.setEnabled(!p.equals(ProfileManager.getDefault().getNextActiveProfile())); 729 } else { 730 this.btnActivateProfile.setEnabled(!p.equals(ProfileManager.getDefault().getActiveProfile())); 731 } 732 this.btnCopyProfile.setEnabled(true); 733 this.btnExportProfile.setEnabled(true); 734 } else if (this.profilesTbl.getSelectedRowCount() > 1) { 735 this.btnDeleteProfile.setEnabled(true); 736 for (int row : this.profilesTbl.getSelectedRows()) { 737 Profile p = ProfileManager.getDefault().getAllProfiles().get(row); 738 if (p.equals(ProfileManager.getDefault().getActiveProfile())) { 739 this.btnDeleteProfile.setEnabled(false); 740 break; 741 } 742 } 743 this.btnCopyProfile.setEnabled(false); 744 this.btnExportProfile.setEnabled(false); 745 this.btnActivateProfile.setEnabled(false); 746 } else { 747 this.btnDeleteProfile.setEnabled(false); 748 this.btnCopyProfile.setEnabled(false); 749 this.btnExportProfile.setEnabled(false); 750 this.btnActivateProfile.setEnabled(false); 751 } 752 } 753 754 private void searchPathsTblValueChanged(ListSelectionEvent e) { 755 if (this.searchPathsTbl.getSelectedRowCount() == 1 && this.searchPathsTbl.getSelectedRow() < ProfileManager.getDefault().getAllSearchPaths().size()) { 756 File sp = ProfileManager.getDefault().getSearchPaths(this.searchPathsTbl.getSelectedRow()); 757 if (sp == null || sp.equals(new File(FileUtil.getPreferencesPath()))) { 758 this.btnRemoveSearchPath.setEnabled(false); 759 } else { 760 this.btnRemoveSearchPath.setEnabled(true); 761 } 762 } else if (this.searchPathsTbl.getSelectedRowCount() > 1) { 763 this.btnRemoveSearchPath.setEnabled(true); 764 } else { 765 this.btnRemoveSearchPath.setEnabled(false); 766 } 767 } 768 769 @Override 770 public boolean isDirty() { 771 return false; // ProfileManager preferences are saved immediately, so this is always false 772 } 773 774 /** 775 * {@inheritDoc} 776 * @return if the next profile to use has changed; false otherwise 777 */ 778 @Override 779 public boolean isRestartRequired() { 780 // Since next profile defaults to null when application starts, restart 781 // is required only if next profile is not null and is not the same 782 // profile as the current profile 783 Profile ap = ProfileManager.getDefault().getActiveProfile(); 784 Profile np = ProfileManager.getDefault().getNextActiveProfile(); 785 return np != null && ap != null && !ap.equals(np); 786 } 787 788 @Override 789 public boolean isPreferencesValid() { 790 return true; // no validity checking performed 791 } 792 793 /* Comment out until I get around to utilizing this, so Jenkins does not throw warnings. 794 private static class ZipFileFilter extends FileFilter { 795 796 public ZipFileFilter() { 797 } 798 799 @Override 800 public boolean accept(File f) { 801 if (!f.isDirectory()) { 802 int i = f.getName().lastIndexOf('.'); 803 if (i > 0 && i < f.getName().length() - 1) { 804 return f.getName().substring(i + 1).toLowerCase().equalsIgnoreCase("zip"); // NOI18N 805 } 806 return false; 807 } 808 return true; 809 } 810 811 @Override 812 public String getDescription() { 813 return "Zip archives (.zip)"; 814 } 815 } 816 */ 817 private class SearchPathSelectionListener implements ListSelectionListener { 818 819 @Override 820 public void valueChanged(ListSelectionEvent e) { 821 ProfilePreferencesPanel.this.searchPathsTblValueChanged(e); 822 } 823 } 824 825 private class ProfilesSelectionListener implements ListSelectionListener { 826 827 @Override 828 public void valueChanged(ListSelectionEvent e) { 829 ProfilePreferencesPanel.this.profilesTblValueChanged(e); 830 } 831 } 832 833 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ProfilePreferencesPanel.class); 834 835}