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