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}