001package jmri.jmrit.whereused;
002
003import java.awt.*;
004import java.awt.event.*;
005import java.io.File;
006import java.io.IOException;
007import javax.swing.*;
008
009import jmri.*;
010import jmri.jmrit.entryexit.DestinationPoints;
011import jmri.jmrit.entryexit.EntryExitPairs;
012import jmri.jmrit.logix.OBlock;
013import jmri.jmrit.logix.OBlockManager;
014import jmri.jmrit.logix.Warrant;
015import jmri.jmrit.logix.WarrantManager;
016import jmri.swing.NamedBeanComboBox;
017import jmri.util.FileUtil;
018import jmri.util.swing.JComboBoxUtil;
019import jmri.util.swing.JmriJOptionPane;
020
021/**
022 * Create a where used report based on the selected bean.  The selection combo box is
023 * based on the selected type.
024
025 * @author Dave Sand Copyright (C) 2020
026 */
027public class WhereUsedFrame extends jmri.util.JmriJFrame {
028    ItemType _itemType = ItemType.NONE;
029    JComboBox<ItemType> _itemTypeBox;
030
031    NamedBean _itemBean;
032    NamedBeanComboBox<?> _itemNameBox = new NamedBeanComboBox<>(
033                        InstanceManager.getDefault(SensorManager.class));
034
035    JPanel _topPanel;
036    JPanel _bottomPanel;
037    JPanel _scrolltext = new JPanel();
038    JTextArea _textArea;
039    JButton _createButton;
040    JLabel itemNameLabel;
041
042    public WhereUsedFrame() {
043        super(true, true);
044        setTitle(Bundle.getMessage("TitleWhereUsed"));  // NOI18N
045        createFrame();
046        addHelpMenu("package.jmri.jmrit.whereused.WhereUsed", true);  // NOI18N
047    }
048
049    /**
050     * Create the window frame.  The top part contains the item type, the item name
051     * combo box, and a Create button.  The middle contains the scrollable "where used" text area and the
052     * bottom part has a button for saving the content to a file.
053     */
054    void createFrame() {
055        Container contentPane = getContentPane();
056        contentPane.setLayout(new BorderLayout());
057
058        // Build the top panel
059        buildTopPanel();
060        contentPane.add(_topPanel, BorderLayout.NORTH);
061
062        // Build an empty where used listing
063        JScrollPane scrollPane;
064        buildWhereUsedListing(ItemType.NONE, null);
065        scrollPane = new JScrollPane(_scrolltext);
066        contentPane.add(scrollPane);
067
068        // Build the bottom panel
069        buildBottomPanel();
070        contentPane.add(_bottomPanel, BorderLayout.SOUTH);
071
072        pack();
073    }
074
075    void buildTopPanel() {
076        _topPanel = new JPanel();
077        JLabel itemTypeLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("LabelItemType")));  // NOI18N
078        _topPanel.add(itemTypeLabel);
079        _itemTypeBox = new JComboBox<>();
080        itemTypeLabel.setLabelFor(_itemTypeBox);
081        for (ItemType itemType : ItemType.values()) {
082            _itemTypeBox.addItem(itemType);
083        }
084        JComboBoxUtil.setupComboBoxMaxRows(_itemTypeBox);
085        _topPanel.add(_itemTypeBox);
086
087        itemNameLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("LabelItemName")));  // NOI18N
088        _topPanel.add(itemNameLabel);
089        itemNameLabel.setLabelFor(_itemNameBox);
090        _topPanel.add(_itemNameBox);
091        _itemTypeBox.addActionListener((e) -> {
092            _itemType = _itemTypeBox.getItemAt(_itemTypeBox.getSelectedIndex());
093            setItemNameBox(_itemType);
094        });
095
096        _createButton = new JButton(Bundle.getMessage("ButtonCreate"));  // NOI18N
097        _createButton.addActionListener((e) -> buildWhereUsedListing(_itemType, _itemBean));
098
099        _topPanel.add(_createButton);
100        _itemNameBox.setEnabled(false);
101        _createButton.setEnabled(false);
102    }
103
104    void buildBottomPanel() {
105        _bottomPanel = new JPanel();
106        _bottomPanel.setLayout(new BorderLayout());
107
108        JButton saveButton = new JButton(Bundle.getMessage("SaveButton"));   // NOI18N
109        saveButton.setToolTipText(Bundle.getMessage("SaveButtonHint"));      // NOI18N
110        _bottomPanel.add(saveButton, BorderLayout.EAST);
111        saveButton.addActionListener((ActionEvent e) -> saveWhereUsedPressed());
112    }
113
114    /**
115     * Create a new NamedBeanComboBox based on the item type and refresh the panel.
116     * A selection listener saves the selection and enables the Create button.
117     * @param itemType The enum for the selected item type.
118     */
119    void setItemNameBox(ItemType itemType) {
120        _createButton.setEnabled(false);
121        buildWhereUsedListing(ItemType.NONE, null);
122        NamedBeanComboBox<?> newNameBox = createNameBox(itemType);
123        if (newNameBox == null) {
124            _itemNameBox.setSelectedIndex(-1);
125            _itemNameBox.setEnabled(false);
126            return;
127        }
128        _itemNameBox = newNameBox;
129        itemNameLabel.setLabelFor(newNameBox);
130        _itemNameBox.setSelectedIndex(-1);
131        _topPanel.remove(3);
132        _topPanel.add(_itemNameBox, 3);
133
134        _itemNameBox.setEnabled(true);
135        _itemNameBox.addItemListener((e) -> {
136            if (e.getStateChange() == ItemEvent.SELECTED) {
137                _itemBean = (NamedBean) e.getItem();
138                _createButton.setEnabled(true);
139            }
140        });
141        pack();
142        repaint();
143    }
144
145    /**
146     * Build the where used content and update the JScrollPane.
147     * <p>
148     * The selected object is passed to the appropriate detail class which returns a populated textarea.
149     * The textarea is formatted and inserted into a scrollable panel.
150     * @param type Indicated type of item being examined
151     * @param bean The bean being examined
152     */
153    void buildWhereUsedListing(ItemType type, NamedBean bean) {
154        switch (type) {
155            case TURNOUT:
156                _textArea = TurnoutWhereUsed.getWhereUsed(bean);
157                break;
158            case SENSOR:
159                _textArea = SensorWhereUsed.getWhereUsed(bean);
160                break;
161            case LIGHT:
162                _textArea = LightWhereUsed.getWhereUsed(bean);
163                break;
164            case SIGNALHEAD:
165                _textArea = SignalHeadWhereUsed.getWhereUsed(bean);
166                break;
167            case SIGNALMAST:
168                _textArea = SignalMastWhereUsed.getWhereUsed(bean);
169                break;
170            case REPORTER:
171                _textArea = ReporterWhereUsed.getWhereUsed(bean);
172                break;
173            case MEMORY:
174                _textArea = MemoryWhereUsed.getWhereUsed(bean);
175                break;
176            case ROUTE:
177                _textArea = RouteWhereUsed.getWhereUsed(bean);
178                break;
179            case OBLOCK:
180                _textArea = OBlockWhereUsed.getWhereUsed(bean);
181                break;
182            case BLOCK:
183                _textArea = BlockWhereUsed.getWhereUsed(bean);
184                break;
185            case SECTION:
186                _textArea = SectionWhereUsed.getWhereUsed(bean);
187                break;
188            case WARRANT:
189                _textArea = WarrantWhereUsed.getWhereUsed(bean);
190                break;
191            case ENTRYEXIT:
192                _textArea = EntryExitWhereUsed.getWhereUsed(bean);
193                break;
194            case AUDIO:
195                _textArea = AudioWhereUsed.getWhereUsed(bean);
196                break;
197            default:
198                _textArea = new JTextArea(Bundle.getMessage("TypePrompt", Bundle.getMessage("ButtonCreate")));
199                break;
200        }
201
202        _textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
203        _textArea.setTabSize(4);
204        _textArea.setEditable(false);
205        _textArea.setCaretPosition(0);
206        if (_scrolltext.getComponentCount() > 0) {
207            _scrolltext.remove(0);
208        }
209        _scrolltext.add(_textArea);
210        pack();
211        repaint();
212    }
213
214    JFileChooser userFileChooser = new jmri.util.swing.JmriJFileChooser(FileUtil.getUserFilesPath());
215
216    /**
217     * Save the where used textarea content to a text file.
218     */
219    void saveWhereUsedPressed() {
220        userFileChooser.setApproveButtonText(Bundle.getMessage("SaveDialogApprove"));  // NOI18N
221        userFileChooser.setDialogTitle(Bundle.getMessage("SaveDialogTitle"));  // NOI18N
222        userFileChooser.rescanCurrentDirectory();
223
224        String itemName = _itemNameBox.getSelectedItemDisplayName();
225        String fileName = Bundle.getMessage("SaveFileName", (itemName == null) ? "Unknown" : itemName);  // NOI18N
226        userFileChooser.setSelectedFile(new File(fileName));
227        int retVal = userFileChooser.showSaveDialog(null);
228        if (retVal != JFileChooser.APPROVE_OPTION) {
229            log.debug("Save where used content stopped, no file selected");  // NOI18N
230            return;  // give up if no file selected or cancel pressed
231        }
232        File file = userFileChooser.getSelectedFile();
233        log.debug("Save where used content to '{}'", file);  // NOI18N
234
235        if (file.exists()) {
236            Object[] options = {Bundle.getMessage("SaveDuplicateReplace"),  // NOI18N
237                    Bundle.getMessage("SaveDuplicateAppend"),  // NOI18N
238                    Bundle.getMessage("ButtonCancel")};               // NOI18N
239            int selectedOption = JmriJOptionPane.showOptionDialog(null,
240                    Bundle.getMessage("SaveDuplicatePrompt", file.getName(),
241                            Bundle.getMessage("SaveDuplicateAppend"),
242                            Bundle.getMessage("SaveDuplicateReplace")), // NOI18N
243                    Bundle.getMessage("SaveDuplicateTitle"),   // NOI18N
244                    JmriJOptionPane.DEFAULT_OPTION,
245                    JmriJOptionPane.WARNING_MESSAGE,
246                    null, options, options[0]);
247            if (selectedOption == 2 || selectedOption == -1) {
248                log.debug("Save where used content stopped, file replace/append cancelled");  // NOI18N
249                return;  // Cancel selected or dialog box closed
250            }
251            if (selectedOption == 0) {
252                FileUtil.delete(file);  // Replace selected
253            }
254        }
255
256        // Create the file content
257        try {
258            FileUtil.appendTextToFile(file, _textArea.getText());
259        } catch (IOException e) {
260            log.error("Unable to write where used content to '{}', exception", file, e);  // NOI18N
261        }
262    }
263
264    /**
265     * Create a combo name box for name selection.
266     *
267     * @param itemType The selected bean type
268     * @return a combo box based on the item type or null if no match
269     */
270    NamedBeanComboBox<?> createNameBox(ItemType itemType) {
271        NamedBeanComboBox<?> nameBox;
272        switch (itemType) {
273            case TURNOUT:
274                nameBox = new NamedBeanComboBox<Turnout>(InstanceManager.getDefault(TurnoutManager.class));
275                break;
276            case SENSOR:
277                nameBox = new NamedBeanComboBox<Sensor>(InstanceManager.getDefault(SensorManager.class));
278                break;
279            case LIGHT:
280                nameBox = new NamedBeanComboBox<Light>(InstanceManager.getDefault(LightManager.class));
281                break;
282            case SIGNALHEAD:
283                nameBox = new NamedBeanComboBox<SignalHead>(InstanceManager.getDefault(SignalHeadManager.class));
284                break;
285            case SIGNALMAST:
286                nameBox = new NamedBeanComboBox<SignalMast>(InstanceManager.getDefault(SignalMastManager.class));
287                break;
288            case REPORTER:
289                nameBox = new NamedBeanComboBox<Reporter>(InstanceManager.getDefault(ReporterManager.class));
290                break;
291            case MEMORY:
292                nameBox = new NamedBeanComboBox<Memory>(InstanceManager.getDefault(MemoryManager.class));
293                break;
294            case ROUTE:
295                nameBox = new NamedBeanComboBox<Route>(InstanceManager.getDefault(RouteManager.class));
296                break;
297            case OBLOCK:
298                nameBox = new NamedBeanComboBox<OBlock>(InstanceManager.getDefault(OBlockManager.class));
299                break;
300            case BLOCK:
301                nameBox = new NamedBeanComboBox<Block>(InstanceManager.getDefault(BlockManager.class));
302                break;
303            case SECTION:
304                nameBox = new NamedBeanComboBox<Section>(InstanceManager.getDefault(SectionManager.class));
305                break;
306            case WARRANT:
307                nameBox = new NamedBeanComboBox<Warrant>(InstanceManager.getDefault(WarrantManager.class));
308                break;
309            case ENTRYEXIT:
310                nameBox = new NamedBeanComboBox<DestinationPoints>(InstanceManager.getDefault(EntryExitPairs.class));
311                break;
312            case AUDIO:
313                nameBox = new NamedBeanComboBox<Audio>(InstanceManager.getDefault(AudioManager.class));
314                break;
315            default:
316                return null;             // Skip any other items.
317        }
318        nameBox.setEditable(false);
319        nameBox.setValidatingInput(false);
320        JComboBoxUtil.setupComboBoxMaxRows(nameBox);
321        return nameBox;
322    }
323
324    /**
325     * The item types.  A bundle key for each type is stored with the type to
326     * create a language dependent toString result.
327     */
328    enum ItemType {
329        NONE("ItemTypeNone"),
330        TURNOUT("BeanNameTurnout"),
331        SENSOR("BeanNameSensor"),
332        LIGHT("BeanNameLight"),
333        SIGNALHEAD("BeanNameSignalHead"),
334        SIGNALMAST("BeanNameSignalMast"),
335        REPORTER("BeanNameReporter"),
336        MEMORY("BeanNameMemory"),
337        ROUTE("BeanNameRoute"),
338        OBLOCK("BeanNameOBlock"),
339        BLOCK("BeanNameBlock"),
340        SECTION("BeanNameSection"),
341        WARRANT("BeanNameWarrant"),
342        ENTRYEXIT("BeanNameEntryExit"),
343        AUDIO("BeanNameAudio");
344
345        private final String _bundleKey;
346
347        ItemType(String bundleKey) {
348            _bundleKey = bundleKey;
349        }
350
351        @Override
352        public String toString() {
353            return Bundle.getMessage(_bundleKey);
354        }
355    }
356
357    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(WhereUsedFrame.class);
358
359}