001package jmri.jmrit.operations; 002 003import java.awt.*; 004import java.awt.event.ActionEvent; 005import java.util.Optional; 006 007import javax.swing.*; 008import javax.swing.colorchooser.AbstractColorChooserPanel; 009import javax.swing.event.ChangeEvent; 010import javax.swing.table.TableModel; 011import javax.swing.table.TableRowSorter; 012 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016import jmri.InstanceManager; 017import jmri.jmrit.operations.rollingstock.cars.CarTypes; 018import jmri.jmrit.operations.setup.Control; 019import jmri.jmrit.operations.trains.TrainCommon; 020import jmri.swing.JTablePersistenceManager; 021import jmri.util.JmriJFrame; 022import jmri.util.swing.SplitButtonColorChooserPanel; 023 024/** 025 * Panel for operations 026 * 027 * @author Dan Boudreau Copyright (C) 2008, 2012 028 */ 029public class OperationsPanel extends JPanel { 030 031 public static final String NEW_LINE = "\n"; // NOI18N 032 public static final String NONE = ""; // NOI18N 033 034 public OperationsPanel() { 035 super(); 036 } 037 038 public void dispose() { 039 // The default method does nothing. 040 } 041 042 /** 043 * Increases the width of the ComboBox to the maximum number of characters 044 * for a standard attribute. This prevents names from being truncated when 045 * displayed. 046 * 047 * @param comboBox the box needing width adjustment 048 */ 049 public static void padComboBox(JComboBox<?> comboBox) { 050 padComboBox(comboBox, Control.max_len_string_attibute + 1); 051 } 052 053 /** 054 * Increases the width of the ComboBox so the names don't get truncated when 055 * displayed. If there are names in the ComboxBox that exceed the character 056 * count, then the wider width is used. 057 * 058 * @param comboBox the box needing width adjustment 059 * @param count the minimum number of characters to display properly 060 */ 061 public static void padComboBox(JComboBox<?> comboBox, int count) { 062 StringBuffer sb = new StringBuffer(); 063 for (int i = 0; i < count; i++) { 064 sb.append("X"); // wider than average 065 } 066 JComboBox<String> pad = new JComboBox<>(); 067 pad.addItem(sb.toString()); 068 if (comboBox.getPreferredSize().getWidth() < pad.getPreferredSize().getWidth()) { 069 Dimension boxDim = 070 new Dimension((int) pad.getPreferredSize().getWidth(), 071 (int) comboBox.getPreferredSize().getHeight()); 072 comboBox.setPreferredSize(boxDim); 073 } 074 } 075 076 protected void addItem(JComponent c, int x, int y) { 077 GridBagConstraints gc = new GridBagConstraints(); 078 gc.gridx = x; 079 gc.gridy = y; 080 gc.weightx = 100.0; 081 gc.weighty = 100.0; 082 this.add(c, gc); 083 } 084 085 protected void addItem(JPanel p, JComponent c, int x, int y) { 086 GridBagConstraints gc = new GridBagConstraints(); 087 gc.gridx = x; 088 gc.gridy = y; 089 gc.weightx = 100.0; 090 gc.weighty = 100.0; 091 p.add(c, gc); 092 } 093 094 protected void addItemLeft(JPanel p, JComponent c, int x, int y) { 095 GridBagConstraints gc = new GridBagConstraints(); 096 gc.gridx = x; 097 gc.gridy = y; 098 gc.weightx = 100.0; 099 gc.weighty = 100.0; 100 gc.anchor = GridBagConstraints.WEST; 101 p.add(c, gc); 102 } 103 104 protected void addItemTop(JPanel p, JComponent c, int x, int y) { 105 GridBagConstraints gc = new GridBagConstraints(); 106 gc.gridx = x; 107 gc.gridy = y; 108 gc.weightx = 100; 109 gc.weighty = 100; 110 gc.anchor = GridBagConstraints.NORTH; 111 p.add(c, gc); 112 } 113 114 protected void addItemWidth(JPanel p, JComponent c, int width, int x, int y) { 115 GridBagConstraints gc = new GridBagConstraints(); 116 gc.gridx = x; 117 gc.gridy = y; 118 gc.gridwidth = width; 119 gc.weightx = 100.0; 120 gc.weighty = 100.0; 121 gc.anchor = GridBagConstraints.WEST; 122 p.add(c, gc); 123 } 124 125 private static final int MIN_CHECKBOXES = 5; 126 private static final int MAX_CHECKBOXES = 11; 127 128 protected int getNumberOfCheckboxesPerLine(Dimension size) { 129 if (size == null) { 130 return MIN_CHECKBOXES; // default is 6 checkboxes per row 131 } 132 StringBuilder padding = new StringBuilder("X"); 133 for (int i = 0; i < InstanceManager.getDefault(CarTypes.class).getMaxFullNameLength(); i++) { 134 padding.append("X"); 135 } 136 137 JCheckBox box = new JCheckBox(padding.toString()); 138 int number = size.width / (box.getPreferredSize().width); 139 if (number < MIN_CHECKBOXES) { 140 number = MIN_CHECKBOXES; 141 } 142 if (number > MAX_CHECKBOXES) { 143 number = MAX_CHECKBOXES; 144 } 145 return number; 146 } 147 148 protected void addButtonAction(JButton b) { 149 b.addActionListener((ActionEvent e) -> { 150 buttonActionPerformed(e); 151 }); 152 } 153 154 protected void buttonActionPerformed(ActionEvent ae) { 155 log.debug("button action not overridden"); 156 } 157 158 protected void addRadioButtonAction(JRadioButton b) { 159 b.addActionListener((ActionEvent e) -> { 160 radioButtonActionPerformed(e); 161 }); 162 } 163 164 protected void radioButtonActionPerformed(ActionEvent ae) { 165 log.debug("radio button action not overridden"); 166 } 167 168 protected void addCheckBoxAction(JCheckBox b) { 169 b.addActionListener((ActionEvent e) -> { 170 checkBoxActionPerformed(e); 171 }); 172 } 173 174 protected void checkBoxActionPerformed(ActionEvent ae) { 175 log.debug("check box action not overridden"); 176 } 177 178 protected void addSpinnerChangeListerner(JSpinner s) { 179 s.addChangeListener((ChangeEvent e) -> { 180 spinnerChangeEvent(e); 181 }); 182 } 183 184 protected void spinnerChangeEvent(javax.swing.event.ChangeEvent ae) { 185 log.debug("spinner action not overridden"); 186 } 187 188 protected void addComboBoxAction(JComboBox<?> b) { 189 b.addActionListener((ActionEvent e) -> { 190 comboBoxActionPerformed(e); 191 }); 192 } 193 194 protected void comboBoxActionPerformed(ActionEvent ae) { 195 log.debug("combobox action not overridden"); 196 } 197 198 protected void selectNextItemComboBox(JComboBox<?> b) { 199 int newIndex = b.getSelectedIndex() + 1; 200 if (newIndex < b.getItemCount()) { 201 b.setSelectedIndex(newIndex); 202 } 203 } 204 205 /** 206 * Will modify the character column width of a TextArea box to 90% of a 207 * panels width. ScrollPane is set to 95% of panel width. 208 * 209 * @param scrollPane the pane containing the textArea 210 * @param textArea the textArea to adjust 211 * @param size the preferred size 212 */ 213 protected void adjustTextAreaColumnWidth(JScrollPane scrollPane, JTextArea textArea, Dimension size) { 214 FontMetrics metrics = getFontMetrics(textArea.getFont()); 215 int columnWidth = metrics.charWidth('m'); 216 int width = size.width; 217 int columns = width / columnWidth * 90 / 100; // make text area 90% of the panel width 218 if (columns > textArea.getColumns()) { 219 log.debug("Increasing text area character width to {} columns", columns); 220 textArea.setColumns(columns); 221 } 222 scrollPane.setMinimumSize(new Dimension(width * 95 / 100, 60)); 223 } 224 225 /** 226 * Load the table width, position, and sorting status from the user 227 * preferences file. 228 * 229 * @param table The table to be adjusted. 230 */ 231 public void loadTableDetails(JTable table) { 232 loadTableDetails(table, getWindowFrameRef()); 233 persist(table); 234 } 235 236 public static void loadTableDetails(JTable table, String name) { 237 if (table.getRowSorter() == null) { 238 TableRowSorter<? extends TableModel> sorter = new TableRowSorter<>(table.getModel()); 239 table.setRowSorter(sorter); 240 // only sort on columns that are String, Integer or Boolean (check boxes) 241 for (int i = 0; i < table.getColumnCount(); i++) { 242 if (table.getColumnClass(i) == String.class || 243 table.getColumnClass(i) == Integer.class || 244 table.getColumnClass(i) == Boolean.class) { 245 continue; // allow sorting 246 } 247 sorter.setSortable(i, false); 248 } 249 } 250 // set row height 251 table.setRowHeight(new JComboBox<>().getPreferredSize().height); 252 // have to shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 541) 253 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 254 // give each cell a bit of space between the vertical lines and text 255 table.setIntercellSpacing(new Dimension(3, 1)); 256 // table must have a name 257 table.setName(name + ":table"); // NOI18N 258 Optional<JTablePersistenceManager> manager = InstanceManager.getOptionalDefault(JTablePersistenceManager.class); 259 if (manager.isPresent()) { 260 manager.get().resetState(table); 261 } 262 } 263 264 public static void persist(JTable table) { 265 Optional<JTablePersistenceManager> manager = InstanceManager.getOptionalDefault(JTablePersistenceManager.class); 266 if (manager.isPresent()) { 267 manager.get().persist(table); 268 } 269 } 270 271 public static void cacheState(JTable table) { 272 Optional<JTablePersistenceManager> manager = InstanceManager.getOptionalDefault(JTablePersistenceManager.class); 273 if (manager.isPresent()) { 274 manager.get().cacheState(table); 275 } 276 } 277 278 public static void saveTableState() { 279 Optional<JTablePersistenceManager> manager = InstanceManager.getOptionalDefault(JTablePersistenceManager.class); 280 if (manager.isPresent()) { 281 manager.get().setPaused(false); // cheater way to save preferences. 282 } 283 } 284 285 protected void clearTableSort(JTable table) { 286 if (table.getRowSorter() != null) { 287 table.getRowSorter().setSortKeys(null); 288 } 289 } 290 291 protected void storeValues() { 292 OperationsXml.save(); 293 } 294 295 /* 296 * Kludge fix for horizontal scrollbar encroaching buttons at bottom of a 297 * scrollable window. 298 */ 299 protected void addHorizontalScrollBarKludgeFix(JScrollPane pane, JPanel panel) { 300 JPanel pad = new JPanel(); // kludge fix for horizontal scrollbar 301 pad.add(new JLabel(" ")); 302 panel.add(pad); 303 304 // make sure control panel is the right size 305 pane.setMinimumSize(new Dimension(500, 130)); 306 pane.setMaximumSize(new Dimension(2000, 170)); 307 pane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); 308 } 309 310 protected String getWindowFrameRef() { 311 Container c = this.getTopLevelAncestor(); 312 if (c instanceof JmriJFrame) { 313 return ((JmriJFrame) c).getWindowFrameRef(); 314 } 315 return null; 316 } 317 318 public static JPanel getColorChooserPanel(String text, JColorChooser chooser) { 319 return getColorChooserPanel(Bundle.getMessage("TextColor"), TrainCommon.getTextColor(text), chooser); 320 } 321 322 public static JPanel getColorChooserPanel(String title, Color color, JColorChooser chooser) { 323 JPanel pTextColorPanel = new JPanel(); 324 pTextColorPanel.setBorder(BorderFactory.createTitledBorder(title)); 325 chooser.setColor(color); 326 AbstractColorChooserPanel commentColorPanels[] = {new SplitButtonColorChooserPanel()}; 327 chooser.setChooserPanels(commentColorPanels); 328 chooser.setPreviewPanel(new JPanel()); 329 pTextColorPanel.add(chooser); 330 return pTextColorPanel; 331 } 332 333 public static void loadFontSizeComboBox(JComboBox<Integer> box) { 334 // load font sizes 7 through 18 335 for (int i = 7; i < 19; i++) { 336 box.addItem(i); 337 } 338 Dimension size = box.getPreferredSize(); 339 size = new Dimension(size.width + 10, size.height); 340 box.setPreferredSize(size); 341 } 342 343 private final static Logger log = LoggerFactory.getLogger(OperationsPanel.class); 344}