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