001package jmri.jmrit.roster.swing; 002 003import java.awt.Component; 004import java.awt.event.ActionEvent; 005import java.util.List; 006 007import javax.swing.Icon; 008 009import jmri.beans.BeanUtil; 010import jmri.jmrit.roster.Roster; 011import jmri.jmrit.roster.RosterEntry; 012import jmri.jmrit.roster.rostergroup.RosterGroupSelector; 013import jmri.util.swing.CountingBusyDialog; 014import jmri.util.swing.JmriAbstractAction; 015import jmri.util.swing.JmriJOptionPane; 016import jmri.util.swing.WindowInterface; 017import jmri.util.ThreadingUtil; 018 019/** 020 * Remove roster group contents, leaving the group 021 * 022 * @author Kevin Dickerson Copyright (C) 2009, 2026 023 * @author Bob Jacobsen Copyright (C) 2026 024 */ 025public class DeleteRosterGroupContentsAction extends JmriAbstractAction { 026 027 public DeleteRosterGroupContentsAction(String s, WindowInterface wi) { 028 super(s, wi); 029 } 030 031 public DeleteRosterGroupContentsAction(String s, Icon i, WindowInterface wi) { 032 super(s, i, wi); 033 } 034 035 /** 036 * @param s Name of this action, e.g. in menus 037 * @param who Component that action is associated with, used to ensure 038 * proper position in of dialog boxes 039 */ 040 public DeleteRosterGroupContentsAction(String s, Component who) { 041 super(s); 042 _who = who; 043 } 044 045 Component _who; 046 CountingBusyDialog dialog; 047 048 /** 049 * Call setParameter("group", oldName) prior to calling 050 * actionPerformed(event) to bypass the roster group selection dialog if the 051 * name of the group to be copied is already known and is not the 052 * selectedRosterGroup property of the WindowInterface. 053 * 054 */ 055 @Override 056 public void actionPerformed(ActionEvent event) { 057 var roster = Roster.getDefault(); 058 String group = null; 059 if (BeanUtil.hasProperty(wi, RosterGroupSelector.SELECTED_ROSTER_GROUP)) { 060 group = (String) BeanUtil.getProperty(wi, RosterGroupSelector.SELECTED_ROSTER_GROUP); 061 } 062 // null might be valid output from getting the selectedRosterGroup, 063 // so we have to check for null again. 064 if (group == null) { 065 group = (String) JmriJOptionPane.showInputDialog(_who, 066 Bundle.getMessage("DeleteRosterGroupContentsDialog"), 067 Bundle.getMessage("DeleteRosterGroupContentsTitle", ""), 068 JmriJOptionPane.INFORMATION_MESSAGE, 069 null, 070 roster.getRosterGroupListWithNoGroup().toArray(), 071 null); 072 } 073 // can't delete the roster itself (ALLENTRIES and null represent the full roster) 074 if (group == null || group.equals(Roster.ALLENTRIES)) { 075 return; 076 } 077 // prompt for one last chance 078 if (!userOK(group)) { 079 return; 080 } 081 082 final String deleteGroup = group; 083 084 ThreadingUtil.newThread(() -> { 085 // delete the roster group contents 086 List<RosterEntry> entries = roster.getEntriesInGroup(deleteGroup); 087 088 dialog = new CountingBusyDialog(null, "Deleting Roster Entries", true, entries.size()); 089 ThreadingUtil.runOnGUIEventually(() -> {dialog.start();}); 090 091 int count = 0; 092 for (RosterEntry entry : entries) { 093 log.info("Deleting entry: {}", entry.getId()); 094 roster.removeEntry(entry); 095 final int thisCount = ++count; 096 ThreadingUtil.runOnGUI(() -> {dialog.count(thisCount);}); 097 } 098 099 roster.writeRoster(); 100 ThreadingUtil.runOnGUIEventually(() -> {dialog.finish();}); 101 }, "Delete Roster Group Contents").start(); 102 103 } 104 105 /** 106 * Can provide some mechanism to prompt for user for one last chance to 107 * change his/her mind 108 * @param entry roster group to confirm deletion of 109 * 110 * @return true if user says to continue 111 */ 112 boolean userOK(String entry) { 113 String[] titles = {Bundle.getMessage("ButtonDelete"), Bundle.getMessage("ButtonCancel")}; 114 // TODO: replace "Are you sure..." string with JPanel containing string 115 // and checkbox silencing this message in the future 116 return ( 0 == // array position 0, ButtonDelete 117 JmriJOptionPane.showOptionDialog(_who, 118 Bundle.getMessage("DeleteRosterGroupContentsSure", entry), 119 Bundle.getMessage("DeleteRosterGroupContentsTitle", entry), 120 JmriJOptionPane.DEFAULT_OPTION, 121 JmriJOptionPane.QUESTION_MESSAGE, 122 null, 123 titles, 124 null)); 125 } 126 127 // never invoked, because we overrode actionPerformed above 128 @Override 129 public jmri.util.swing.JmriPanel makePanel() { 130 throw new IllegalArgumentException("Should not be invoked"); 131 } 132 133 // initialize logging 134 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DeleteRosterGroupContentsAction.class); 135 136}