001package jmri.jmrit.beantable.usermessagepreferences; 002 003import java.awt.BorderLayout; 004import java.awt.Dimension; 005import java.awt.event.ActionEvent; 006import java.beans.PropertyChangeEvent; 007import java.util.ArrayList; 008import java.util.HashMap; 009import java.util.Map.Entry; 010import javax.swing.BorderFactory; 011import javax.swing.BoxLayout; 012import javax.swing.JCheckBox; 013import javax.swing.JComboBox; 014import javax.swing.JComponent; 015import javax.swing.JLabel; 016import javax.swing.JPanel; 017import javax.swing.JScrollPane; 018import javax.swing.JTabbedPane; 019import jmri.UserPreferencesManager; 020import jmri.jmrit.beantable.AudioTableAction; 021import jmri.jmrit.beantable.BlockTableAction; 022import jmri.jmrit.beantable.IdTagTableAction; 023import jmri.jmrit.beantable.LRouteTableAction; 024import jmri.jmrit.beantable.LightTableAction; 025import jmri.jmrit.beantable.LogixTableAction; 026import jmri.jmrit.beantable.MemoryTableAction; 027import jmri.jmrit.beantable.ReporterTableAction; 028import jmri.jmrit.beantable.RouteTableAction; 029import jmri.jmrit.beantable.SectionTableAction; 030import jmri.jmrit.beantable.SensorTableAction; 031import jmri.jmrit.beantable.SignalGroupTableAction; 032import jmri.jmrit.beantable.SignalHeadTableAction; 033import jmri.jmrit.beantable.SignalMastTableAction; 034import jmri.jmrit.beantable.TransitTableAction; 035import jmri.jmrit.beantable.TurnoutTableAction; 036import jmri.jmrit.logixng.LogixNG_UserPreferences; 037import jmri.swing.PreferencesPanel; 038import jmri.util.swing.JmriPanel; 039import org.openide.util.lookup.ServiceProvider; 040 041/** 042 * Pane to show User Message Preferences. 043 * 044 * <p>Class based preferences are returned from {@link jmri.managers.JmriUserPreferencesManager}. 045 * If a class has more than one possible message, it will have its own tab.</p> 046 * 047 * <p>Classes with a single possible message will be in the <b>Misc items</b> tab. These are only 048 * shown when active.</p> 049 * 050 * <p>The <b>Misc items</b> tab includes an option to show only tabs that have active messages. The 051 * normal tabs list is quite long.</p> 052 * 053 * @author Kevin Dickerson Copyright (C) 2009 054 */ 055@ServiceProvider(service = PreferencesPanel.class) 056public class UserMessagePreferencesPane extends JmriPanel implements PreferencesPanel { 057 058 UserPreferencesManager p; 059 060 JTabbedPane tab = new JTabbedPane(); 061 062 private HashMap<JComboBox<Object>, ListItems> _comboBoxes = new HashMap<>(); 063 private HashMap<JCheckBox, ListItems> _checkBoxes = new HashMap<>(); 064 065 private JCheckBox tabsOption = new JCheckBox(Bundle.getMessage("ShowTabsCheckBox")); 066 067 public UserMessagePreferencesPane() { 068 super(); 069 p = jmri.InstanceManager.getDefault(UserPreferencesManager.class); 070 p.addPropertyChangeListener((PropertyChangeEvent e) -> { 071 if (e.getPropertyName().equals(UserPreferencesManager.PREFERENCES_UPDATED)) { 072 refreshOptions(); 073 } 074 }); 075 076 tabsOption.setSelected(p.getCheckboxPreferenceState(getClassName() + ".tabsOption", false)); 077 tabsOption.addActionListener((ActionEvent e) -> { 078 p.setCheckboxPreferenceState(getClassName() + ".tabsOption", tabsOption.isSelected()); 079 }); 080 081 if (!tabsOption.isSelected()) { 082 setMinimumMessagePref(); 083 } 084 085 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 086 newMessageTab(); 087 add(tab); 088 } 089 090 private synchronized void setMinimumMessagePref() { 091 //This ensures that as a minimum that the following items are at least initialised and appear in the preference panel 092 p.setClassDescription(AudioTableAction.class.getName()); 093 p.setClassDescription(BlockTableAction.class.getName()); 094 p.setClassDescription(IdTagTableAction.class.getName()); 095 096 p.setClassDescription(LightTableAction.class.getName()); 097 p.setClassDescription(LogixTableAction.class.getName()); 098 p.setClassDescription(LRouteTableAction.class.getName()); 099 p.setClassDescription(MemoryTableAction.class.getName()); 100 101 p.setClassDescription(ReporterTableAction.class.getName()); 102 p.setClassDescription(RouteTableAction.class.getName()); 103 104 p.setClassDescription(SectionTableAction.class.getName()); 105 p.setClassDescription(SensorTableAction.class.getName()); 106 p.setClassDescription(SignalGroupTableAction.class.getName()); 107 p.setClassDescription(SignalHeadTableAction.class.getName()); 108 p.setClassDescription(SignalMastTableAction.class.getName()); 109 110 p.setClassDescription(TransitTableAction.class.getName()); 111 p.setClassDescription(TurnoutTableAction.class.getName()); 112 113 p.setClassDescription(LogixNG_UserPreferences.class.getName()); 114 } 115 116 private synchronized void newMessageTab() { 117 remove(tab); 118 tab = new JTabbedPane(); 119 120 //might need to redo this so that it doesn't recreate everything all the time. 121 _comboBoxes = new HashMap<>(); 122 _checkBoxes = new HashMap<>(); 123 124 ArrayList<String> preferenceClassList = p.getPreferencesClasses(); 125 java.util.Collections.sort(preferenceClassList); 126 for (String strClass : preferenceClassList) { 127 128 JPanel classholder = new JPanel(); 129 classholder.setLayout(new BorderLayout()); 130 131 HashMap<Integer, String> options; 132 boolean add = false; 133 boolean addtoindependant = false; 134 if (p.getPreferencesSize(strClass) > 1) { 135 addtoindependant = true; 136 } 137 JPanel classPanel = new JPanel(); 138 classPanel.setLayout(new BoxLayout(classPanel, BoxLayout.Y_AXIS)); 139 classPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 140 for (int j = 0; j < p.getMultipleChoiceSize(strClass); j++) { 141 String itemName = p.getChoiceName(strClass, j); 142 options = p.getChoiceOptions(strClass, itemName); 143 if (options != null) { 144 JComboBox<Object> optionBox = new JComboBox<>(); 145 ListItems li = new ListItems(strClass, itemName); 146 _comboBoxes.put(optionBox, li); 147 li.isIncluded(addtoindependant); 148 optionBox.removeAllItems(); 149 for (Object value : options.values()) { 150 optionBox.addItem(value); 151 } 152 int current = p.getMultipleChoiceOption(strClass, itemName); 153 154 if (options.containsKey(current)) { 155 optionBox.setSelectedItem(options.get(current)); 156 } 157 if (addtoindependant) { 158 JPanel optionPanel = new JPanel(); 159 JLabel _comboLabel = new JLabel(p.getChoiceDescription(strClass, itemName), JLabel.LEFT); 160 _comboLabel.setAlignmentX(0.5f); 161 optionPanel.add(_comboLabel); 162 optionPanel.add(optionBox); 163 add = true; 164 classPanel.add(optionPanel); 165 } 166 } 167 } 168 ArrayList<String> singleList = p.getPreferenceList(strClass); 169 if (!singleList.isEmpty()) { 170 for (int i = 0; i < singleList.size(); i++) { 171 String itemName = p.getPreferenceItemName(strClass, i); 172 String description = p.getPreferenceItemDescription(strClass, itemName); 173 if ((description != null) && (!description.isEmpty())) { 174 JCheckBox check = new JCheckBox(description); 175 check.setSelected(p.getPreferenceState(strClass, itemName)); 176 ListItems li = new ListItems(strClass, itemName); 177 _checkBoxes.put(check, li); 178 li.isIncluded(addtoindependant); 179 180 if (addtoindependant) { 181 classPanel.add(check); 182 add = true; 183 } 184 } 185 } 186 } 187 if (add) { 188 classholder.add(classPanel, BorderLayout.NORTH); 189 if (p.getPreferencesSize(strClass) > 1) { 190 JScrollPane scrollPane = new JScrollPane(classholder); 191 scrollPane.setPreferredSize(new Dimension(300, 300)); 192 scrollPane.setBorder(BorderFactory.createEmptyBorder()); 193 tab.add(scrollPane, p.getClassDescription(strClass)); 194 } 195 } 196 } 197 HashMap<String, ArrayList<ListItems>> countOfItems = new HashMap<>(); 198 HashMap<String, ArrayList<JCheckBox>> countOfItemsCheck = new HashMap<>(); 199 HashMap<String, ArrayList<JComboBox<Object>>> countOfItemsCombo = new HashMap<>(); 200 201 for (Entry<JComboBox<Object>, ListItems> entry : _comboBoxes.entrySet()) { 202 if (!entry.getValue().isIncluded()) { 203 String strItem = entry.getValue().getItem(); 204 if (!countOfItems.containsKey(strItem)) { 205 countOfItems.put(strItem, new ArrayList<>()); 206 countOfItemsCombo.put(strItem, new ArrayList<>()); 207 } 208 ArrayList<ListItems> a = countOfItems.get(strItem); 209 a.add(entry.getValue()); 210 211 ArrayList<JComboBox<Object>> acb = countOfItemsCombo.get(strItem); 212 acb.add(entry.getKey()); 213 } 214 } 215 216 for (Entry<JCheckBox, ListItems> entry : _checkBoxes.entrySet()) { 217 if (!entry.getValue().isIncluded()) { 218 String strItem = entry.getValue().getItem(); 219 if (!countOfItems.containsKey(strItem)) { 220 countOfItems.put(strItem, new ArrayList<>()); 221 countOfItemsCheck.put(strItem, new ArrayList<>()); 222 } 223 ArrayList<ListItems> a = countOfItems.get(strItem); 224 a.add(entry.getValue()); 225 226 ArrayList<JCheckBox> acb = countOfItemsCheck.get(strItem); 227 acb.add(entry.getKey()); 228 } 229 } 230 231 JPanel miscPanel = new JPanel(); 232 miscPanel.setLayout(new BoxLayout(miscPanel, BoxLayout.Y_AXIS)); 233 234 JPanel mischolder = new JPanel(); 235 mischolder.setLayout(new BorderLayout()); 236 mischolder.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 237 for (Entry<String, ArrayList<ListItems>> entry : countOfItems.entrySet()) { 238 ArrayList<ListItems> a = entry.getValue(); 239 ArrayList<JCheckBox> chb = countOfItemsCheck.get(entry.getKey()); 240 ArrayList<JComboBox<Object>> cob = countOfItemsCombo.get(entry.getKey()); 241 if (a.size() > 1) { 242 JPanel tableDeleteTabPanel = new JPanel(); 243 tableDeleteTabPanel.setLayout(new BoxLayout(tableDeleteTabPanel, BoxLayout.Y_AXIS)); 244 JLabel tableDeleteInfoLabel = new JLabel(p.getChoiceDescription(a.get(0).getClassName(), a.get(0).getItem()), JLabel.CENTER); 245 tableDeleteInfoLabel.setAlignmentX(0.5f); 246 tableDeleteTabPanel.add(tableDeleteInfoLabel); 247 JPanel inside = new JPanel(); 248 if (cob != null) { 249 JPanel insideCombo = new JPanel(); 250 int gridsize = (int) (Math.ceil((cob.size() / 2.0))); 251 insideCombo.setLayout(new jmri.util.javaworld.GridLayout2(gridsize, 2 * 2, 10, 2)); 252 for (JComboBox<Object> combo : cob) { 253 JLabel _comboLabel = new JLabel(p.getClassDescription(_comboBoxes.get(combo).getClassName()), JLabel.RIGHT); 254 _comboBoxes.get(combo).isIncluded(true); 255 insideCombo.add(_comboLabel); 256 insideCombo.add(combo); 257 } 258 inside.add(insideCombo); 259 } 260 if (chb != null) { 261 JPanel insideCheck = new JPanel(); 262 insideCheck.setLayout(new jmri.util.javaworld.GridLayout2(chb.size(), 1)); 263 for (JCheckBox check : chb) { 264 JLabel _checkLabel = new JLabel(p.getClassDescription(_checkBoxes.get(check).getClassName()), JLabel.RIGHT); 265 _checkBoxes.get(check).isIncluded(true); 266 insideCheck.add(_checkLabel); 267 insideCheck.add(check); 268 } 269 inside.add(insideCheck); 270 } 271 tableDeleteTabPanel.add(inside); 272 JScrollPane scrollPane = new JScrollPane(tableDeleteTabPanel); 273 scrollPane.setPreferredSize(new Dimension(300, 300)); 274 tab.add(scrollPane, entry.getKey()); 275 } else { 276 JPanel itemPanel = new JPanel(); 277 JPanel subItem = new JPanel(); 278 subItem.setLayout(new BoxLayout(subItem, BoxLayout.Y_AXIS)); 279 subItem.add(new JLabel(p.getClassDescription(a.get(0).getClassName()), JLabel.CENTER)); 280 281 if (countOfItemsCheck.containsKey(entry.getKey())) { 282 subItem.add(countOfItemsCheck.get(entry.getKey()).get(0)); 283 itemPanel.add(subItem); 284 miscPanel.add(itemPanel); 285 } 286 } 287 288 // Include the tabs option 289 JPanel activePanel = new JPanel(); 290 JPanel activeItem = new JPanel(); 291 activeItem.setLayout(new BoxLayout(activeItem, BoxLayout.Y_AXIS)); 292 activeItem.setBorder(BorderFactory.createLineBorder(java.awt.Color.black)); 293 activeItem.add(new JLabel(Bundle.getMessage("TabsOption")), JLabel.CENTER); 294 activeItem.add(tabsOption); 295 activePanel.add(activeItem); 296 mischolder.add(activePanel); 297 298 } 299 300 add(tab); 301 mischolder.add(miscPanel, BorderLayout.NORTH); 302 JScrollPane miscScrollPane = new JScrollPane(mischolder); 303 miscScrollPane.setPreferredSize(new Dimension(300, 300)); 304 305 tab.add(miscScrollPane, Bundle.getMessage("MiscItems")); 306 revalidate(); 307 } 308 309 @Override 310 public String getPreferencesItem() { 311 return "MESSAGES"; // NOI18N 312 } 313 314 @Override 315 public String getPreferencesItemText() { 316 return Bundle.getMessage("MenuMessages"); // NOI18N 317 } 318 319 @Override 320 public String getTabbedPreferencesTitle() { 321 return null; 322 } 323 324 @Override 325 public String getLabelKey() { 326 return null; 327 } 328 329 @Override 330 public JComponent getPreferencesComponent() { 331 return this; 332 } 333 334 @Override 335 public boolean isPersistant() { 336 return false; 337 } 338 339 @Override 340 public String getPreferencesTooltip() { 341 return null; 342 } 343 344 @Override 345 public void savePreferences() { 346 this.updateManager(); 347 } 348 349 @Override 350 public synchronized boolean isDirty() { 351 for (Entry<JComboBox<Object>, ListItems> entry : _comboBoxes.entrySet()) { 352 String strClass = entry.getValue().getClassName(); 353 String strItem = entry.getValue().getItem(); 354 if (!p.getChoiceOptions(strClass, strItem).get(p.getMultipleChoiceOption(strClass, strItem)).equals(entry.getKey().getSelectedItem())) { 355 return true; 356 } 357 } 358 for (Entry<JCheckBox, ListItems> entry : _checkBoxes.entrySet()) { 359 String strClass = entry.getValue().getClassName(); 360 String strItem = entry.getValue().getItem(); 361 if (p.getPreferenceState(strClass, strItem) != entry.getKey().isSelected()) { 362 return true; 363 } 364 } 365 return false; 366 } 367 368 @Override 369 public boolean isRestartRequired() { 370 return false; 371 } 372 373 @Override 374 public boolean isPreferencesValid() { 375 return true; // no validity checking performed 376 } 377 378 static class ListItems { 379 380 String strClass; 381 String item; 382 boolean included = false; 383 384 ListItems(String strClass, String item) { 385 this.strClass = strClass; 386 this.item = item; 387 } 388 389 String getClassName() { 390 return strClass; 391 } 392 393 String getItem() { 394 return item; 395 } 396 397 boolean isIncluded() { 398 return included; 399 } 400 401 void isIncluded(boolean boo) { 402 included = boo; 403 } 404 } 405 406 boolean updating = false; 407 408 public synchronized void updateManager() { 409 updating = true; 410 p.setLoading(); 411 412 for (Entry<JComboBox<Object>, ListItems> entry : _comboBoxes.entrySet()) { 413 String strClass = entry.getValue().getClassName(); 414 String strItem = entry.getValue().getItem(); 415 String strSelection = (String) entry.getKey().getSelectedItem(); 416 p.setMultipleChoiceOption(strClass, strItem, strSelection); 417 } 418 419 for (Entry<JCheckBox, ListItems> entry : _checkBoxes.entrySet()) { 420 String strClass = entry.getValue().getClassName(); 421 String strItem = entry.getValue().getItem(); 422 p.setPreferenceState(strClass, strItem, entry.getKey().isSelected()); 423 } 424 425 updating = false; 426 p.finishLoading(); 427 refreshOptions(); 428 } 429 430 protected String getClassName() { 431 return UserMessagePreferencesPane.class.getName(); 432 } 433 434 private void refreshOptions() { 435 if (updating) { 436 return; 437 } 438 newMessageTab(); 439 } 440 441}