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