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