001package jmri.jmrit.vsdecoder.swing;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004
005import java.awt.Dimension;
006import java.awt.GridBagLayout;
007import java.awt.event.ActionEvent;
008import java.awt.event.ActionListener;
009import java.awt.event.KeyEvent;
010import java.util.ArrayList;
011import java.util.HashMap;
012import java.util.List;
013import java.util.Map;
014
015import javax.swing.BoxLayout;
016import javax.swing.JButton;
017import javax.swing.JMenu;
018import javax.swing.JMenuBar;
019import javax.swing.JPanel;
020import javax.swing.JScrollPane;
021import javax.swing.JTabbedPane;
022import javax.swing.JTable;
023
024import jmri.Block;
025import jmri.BlockManager;
026import jmri.Reporter;
027import jmri.ReporterManager;
028import jmri.jmrit.operations.OperationsXml;
029import jmri.jmrit.operations.locations.Location;
030import jmri.jmrit.operations.locations.LocationManager;
031import jmri.jmrit.operations.setup.Setup;
032import jmri.jmrit.vsdecoder.VSDecoderManager;
033import jmri.jmrit.vsdecoder.listener.ListeningSpot;
034import jmri.util.PhysicalLocation;
035import jmri.util.swing.JmriJOptionPane;
036
037/**
038 * GUI to manage Reporters, Blocks, Locations and Listener attributes.
039 *
040 * <hr>
041 * This file is part of JMRI.
042 * <p>
043 * JMRI is free software; you can redistribute it and/or modify it under
044 * the terms of version 2 of the GNU General Public License as published
045 * by the Free Software Foundation. See the "COPYING" file for a copy
046 * of this license.
047 * <p>
048 * JMRI is distributed in the hope that it will be useful, but WITHOUT
049 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
050 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
051 * for more details.
052 *
053 * @author Mark Underwood Copyright (C) 2011
054 */
055public class ManageLocationsFrame extends jmri.util.JmriJFrame {
056
057    // Map of Mnemonic KeyEvent values to GUI Components
058    private static final Map<String, Integer> Mnemonics = new HashMap<>();
059
060    static {
061        Mnemonics.put("ReporterTab", KeyEvent.VK_E); // NOI18N
062        Mnemonics.put("OpsTab", KeyEvent.VK_P); // NOI18N
063        Mnemonics.put("ListenerTab", KeyEvent.VK_L); // NOI18N
064        Mnemonics.put("BlockTab", KeyEvent.VK_B); // NOI18N
065        Mnemonics.put("CloseButton", KeyEvent.VK_O); // NOI18N
066        Mnemonics.put("SaveButton", KeyEvent.VK_S); // NOI18N
067    }
068
069    private JTabbedPane tabbedPane;
070    private JPanel listenerPanel;
071    private JPanel reporterPanel;
072    private JPanel opsPanel;
073    private JPanel blockPanel;
074
075    private Object[][] reporterData;  // positions of Reporters
076    private Object[][] opsData;       // positions of Operations Locations
077    private Object[][] locData;       // positions of Listener Locations
078    private Object[][] blockData;     // positions of Blocks
079    private ManageLocationsTableModel.ReporterBlockTableModel reporterModel;
080    private ManageLocationsTableModel.LocationTableModel opsModel;
081    private ManageLocationsTableModel.ListenerTableModel locModel;
082    private ManageLocationsTableModel.ReporterBlockTableModel blockModel;
083    private ListeningSpot listenerLoc;
084
085    private HashMap<String, PhysicalLocation> data;
086
087    private List<JMenu> menuList;
088
089    @SuppressFBWarnings(value = "EI_EXPOSE_REP2",
090            justification = "2D array of different types passed as complex parameter. "
091            + "Better to switch to passing use-specific objects rather than "
092            + "papering this over with a deep copy of the arguments. "
093            + "In any case, there's no risk of exposure here.")
094    public ManageLocationsFrame(ListeningSpot listener,
095            Object[][] reporters,
096            Object[][] ops,
097            Object[][] blocks) {
098        super(true, true);
099        reporterData = reporters;
100        opsData = ops;
101        listenerLoc = listener;
102        blockData = blocks;
103        initGui();
104    }
105
106    private void initGui() {
107
108        this.setTitle(Bundle.getMessage("FieldManageLocationsFrameTitle"));
109        this.buildMenu();
110        // Panel for managing listeners
111        listenerPanel = new JPanel();
112        listenerPanel.setLayout(new BoxLayout(listenerPanel, BoxLayout.Y_AXIS));
113
114        // Build Listener Locations Table
115        locData = new Object[1][7];
116        locData[0][ManageLocationsTableModel.NAMECOL] = listenerLoc.getName();
117        locData[0][ManageLocationsTableModel.USECOL] = true;
118        locData[0][ManageLocationsTableModel.XCOL] = listenerLoc.getLocation().x;
119        locData[0][ManageLocationsTableModel.YCOL] = listenerLoc.getLocation().y;
120        locData[0][ManageLocationsTableModel.ZCOL] = listenerLoc.getLocation().z;
121        locData[0][ManageLocationsTableModel.BEARINGCOL] = listenerLoc.getBearing();
122        locData[0][ManageLocationsTableModel.AZIMUTHCOL] = listenerLoc.getAzimuth();
123
124        log.debug("Listener: {}", listenerLoc.toString());
125        log.debug("  locData:");
126        for (int i = 0; i < 7; i++) {
127            log.debug("  item {}", locData[0][i]);
128        }
129
130        JPanel locPanel = new JPanel();
131        locPanel.setLayout(new BoxLayout(locPanel, BoxLayout.LINE_AXIS));
132        JScrollPane locScrollPanel = new JScrollPane();
133        locModel = new ManageLocationsTableModel.ListenerTableModel(locData);
134        JTable locTable = new JTable(locModel);
135        locTable.setFillsViewportHeight(true);
136        locTable.setPreferredScrollableViewportSize(new Dimension(520, 200));
137        locScrollPanel.getViewport().add(locTable);
138
139        listenerPanel.add(locScrollPanel);
140
141        reporterPanel = new JPanel();
142        reporterPanel.setLayout(new GridBagLayout());
143        JScrollPane reporterScrollPanel = new JScrollPane();
144        reporterModel = new ManageLocationsTableModel.ReporterBlockTableModel(reporterData);
145        JTable reporterTable = new JTable(reporterModel);
146        reporterTable.setFillsViewportHeight(true);
147        reporterTable.setPreferredScrollableViewportSize(new Dimension(540, 200));
148        reporterScrollPanel.getViewport().add(reporterTable);
149
150        blockPanel = new JPanel();
151        blockPanel.setLayout(new GridBagLayout());
152        JScrollPane blockScrollPanel = new JScrollPane();
153        blockModel = new ManageLocationsTableModel.ReporterBlockTableModel(blockData);
154        JTable blockTable = new JTable(blockModel);
155        blockTable.setFillsViewportHeight(true);
156        blockTable.setPreferredScrollableViewportSize(new Dimension(540, 200));
157        blockScrollPanel.getViewport().add(blockTable);
158
159        opsPanel = new JPanel();
160        opsPanel.setLayout(new GridBagLayout());
161        opsPanel.revalidate();
162        JScrollPane opsScrollPanel = new JScrollPane();
163        opsModel = new ManageLocationsTableModel.LocationTableModel(opsData);
164        JTable opsTable = new JTable(opsModel);
165        opsTable.setFillsViewportHeight(true);
166        opsTable.setPreferredScrollableViewportSize(new Dimension(520, 200));
167        opsScrollPanel.getViewport().add(opsTable);
168
169        tabbedPane = new JTabbedPane();
170        tabbedPane.addTab(Bundle.getMessage("Reporters"), reporterScrollPanel); // Reporters Tab Title
171        tabbedPane.setToolTipTextAt(0, Bundle.getMessage("ToolTipReporterTab"));
172        tabbedPane.setMnemonicAt(0, Mnemonics.get("ReporterTab")); // NOI18N
173        tabbedPane.addTab(Bundle.getMessage("Blocks"), blockScrollPanel);
174        tabbedPane.setToolTipTextAt(1, Bundle.getMessage("ToolTipBlockTab"));
175        tabbedPane.setMnemonicAt(1, Mnemonics.get("BlockTab")); // NOI18N
176        tabbedPane.addTab(Bundle.getMessage("FieldOpsTabTitle"), opsScrollPanel);
177        tabbedPane.setToolTipTextAt(2, Bundle.getMessage("ToolTipOpsTab"));
178        tabbedPane.setMnemonicAt(2, Mnemonics.get("OpsTab")); // NOI18N
179        tabbedPane.addTab(Bundle.getMessage("FieldListenersTabTitle"), listenerPanel);
180        tabbedPane.setToolTipTextAt(3, Bundle.getMessage("ToolTipListenerTab"));
181        tabbedPane.setMnemonicAt(3, Mnemonics.get("ListenerTab")); // NOI18N
182
183        JPanel buttonPane = new JPanel();
184        buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
185        JButton closeButton = new JButton(Bundle.getMessage("ButtonCancel"));
186        closeButton.setToolTipText(Bundle.getMessage("ToolTipButtonMLFClose"));
187        closeButton.setMnemonic(Mnemonics.get("CloseButton")); // NOI18N
188        closeButton.addActionListener(new ActionListener() {
189            @Override
190            public void actionPerformed(ActionEvent e) {
191                closeButtonPressed(e);
192            }
193        });
194        JButton saveButton = new JButton(Bundle.getMessage("ButtonSave"));
195        saveButton.setToolTipText(Bundle.getMessage("ToolTipButtonMLFSave"));
196        saveButton.setMnemonic(Mnemonics.get("SaveButton")); // NOI18N
197        saveButton.addActionListener(new ActionListener() {
198            @Override
199            public void actionPerformed(ActionEvent e) {
200                saveButtonPressed(e);
201            }
202        });
203        buttonPane.add(closeButton);
204        buttonPane.add(saveButton);
205
206        this.getContentPane().setLayout(new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS));
207        this.getContentPane().add(tabbedPane);
208        this.getContentPane().add(buttonPane);
209        this.pack();
210        this.setVisible(true);
211    }
212
213    private void buildMenu() {
214        JMenu editMenu = new JMenu(Bundle.getMessage("MenuEdit"));
215        editMenu.add(new VSDPreferencesAction(Bundle.getMessage("VSDecoderFileMenuPreferences")));
216
217        menuList = new ArrayList<>(1);
218
219        menuList.add(editMenu);
220
221        this.setJMenuBar(new JMenuBar());
222        this.getJMenuBar().add(editMenu);
223        this.addHelpMenu("package.jmri.jmrit.vsdecoder.swing.ManageLocationsFrame", true); // NOI18N
224    }
225
226    private void saveButtonPressed(ActionEvent e) {
227        int value = JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("FieldMLFSaveDialogConfirmMessage"),
228                Bundle.getMessage("FieldMLFSaveDialogTitle"),
229                JmriJOptionPane.YES_NO_OPTION);
230        if (value == JmriJOptionPane.YES_OPTION) {
231            saveTableValues();
232            OperationsXml.save();
233        }
234        if (Setup.isCloseWindowOnSaveEnabled()) {
235            dispose();
236        }
237    }
238
239    @SuppressFBWarnings(value = "WMI_WRONG_MAP_ITERATOR", justification = "only in slow debug")
240    private void saveTableValues() {
241        if ((Boolean) locModel.getValueAt(0, ManageLocationsTableModel.USECOL)) {
242            // Don't accept Azimuth value 90 or -90 (they are not in the domain of definition)
243            if ((Double) locModel.getValueAt(0, ManageLocationsTableModel.AZIMUTHCOL) != null
244                    && ((Double) locModel.getValueAt(0, ManageLocationsTableModel.AZIMUTHCOL) == 90.0d
245                    || (Double) locModel.getValueAt(0, ManageLocationsTableModel.AZIMUTHCOL) == -90.0d)) {
246                JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("FieldTableAzimuthInvalidValue"));
247            } else {
248                listenerLoc.setLocation((Double) locModel.getValueAt(0, ManageLocationsTableModel.XCOL),
249                        (Double) locModel.getValueAt(0, ManageLocationsTableModel.YCOL),
250                        (Double) locModel.getValueAt(0, ManageLocationsTableModel.ZCOL));
251                listenerLoc.setOrientation((Double) locModel.getValueAt(0, ManageLocationsTableModel.BEARINGCOL),
252                        (Double) locModel.getValueAt(0, ManageLocationsTableModel.AZIMUTHCOL));
253                VSDecoderManager.instance().getVSDecoderPreferences().save();
254                VSDecoderManager.instance().getVSDecoderPreferences().setListenerPosition(listenerLoc);
255            }
256        }
257
258        data = reporterModel.getDataMap();
259        ReporterManager mgr = jmri.InstanceManager.getDefault(ReporterManager.class);
260        for (String s : data.keySet()) {
261            log.debug("Reporter: {}, Location: {}", s, data.get(s));
262            Reporter r = mgr.getByDisplayName(s);
263            if (r != null) {
264                PhysicalLocation.setBeanPhysicalLocation(data.get(s), r);
265            }
266        }
267
268        data = blockModel.getDataMap();
269        BlockManager bmgr = jmri.InstanceManager.getDefault(BlockManager.class);
270        for (String s : data.keySet()) {
271            log.debug("Block: {}, Location: {}", s, data.get(s));
272            Block b = bmgr.getByDisplayName(s);
273            if (b != null) {
274                PhysicalLocation.setBeanPhysicalLocation(data.get(s), b);
275            }
276        }
277
278        data = opsModel.getDataMap();
279        LocationManager lmgr = jmri.InstanceManager.getDefault(LocationManager.class);
280        for (String s : data.keySet()) {
281            log.debug("OpsLocation: {}, Location: {}", s, data.get(s));
282            Location l = lmgr.getLocationByName(s);
283            l.setPhysicalLocation(data.get(s));
284        }
285    }
286
287    private void closeButtonPressed(ActionEvent e) {
288        dispose();
289    }
290
291    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ManageLocationsFrame.class);
292
293}