001package jmri.jmrix.dccpp.swing.messages; 002 003import java.awt.*; 004import java.io.File; 005import java.util.List; 006 007import javax.annotation.CheckForNull; 008import javax.swing.*; 009 010import jmri.jmrit.display.Editor; 011import jmri.jmrit.display.Positionable; 012import jmri.jmrit.display.PositionableFactory.DoAfter; 013import jmri.jmrix.dccpp.DCCppSystemConnectionMemo; 014import jmri.util.FileUtil; 015import jmri.util.JmriJFrame; 016import jmri.util.swing.JmriColorChooser; 017import jmri.util.swing.JmriJOptionPane; 018import jmri.util.swing.WrapLayout; 019 020/** Settings dialog for a DCC-EX Messages window or panel icon. */ 021public class ConfigureMessages extends JmriJFrame { 022 023 private static ConfigureMessages editPositionableFrame; 024 025 private final Editor editor; 026 private final MessagesConfiguration messagesConfiguration; 027 private final MessagesPositionable newPositionable; 028 private final DoAfter doAfter; 029 private final Runnable afterApply; 030 031 private final JComboBox<DCCppConnection> _memoComboBox = new JComboBox<>(); 032 final JTextField _expiryTextField = new JTextField(5); 033 final JTextField _maxLinesTextField = new JTextField(5); 034 final JCheckBox _autoOpenCheckBox = new JCheckBox(); 035 final JCheckBox _playSoundCheckBox = new JCheckBox(); 036 final JTextField _soundFileTextField = new JTextField(25); 037 final JTextField _columnsTextField = new JTextField(5); 038 final JTextField _linesTextField = new JTextField(5); 039 Color _foreground; 040 Color _background; 041 042 public static void createConfigureMessages(Editor editor, DoAfter doAfter) { 043 closeDialog(null); 044 MessagesPositionable pos = new MessagesPositionable(editor); 045 editPositionableFrame = new ConfigureMessages(editor, pos.getMessagesPanel(), pos, doAfter, null); 046 editPositionableFrame.initComponents(); 047 } 048 049 public static void editConfigureMessages(@CheckForNull Editor editor, MessagesConfiguration config) { 050 editConfigureMessages(editor, config, null); 051 } 052 053 /** Open the settings dialog; afterApply runs once OK has applied the settings. */ 054 public static void editConfigureMessages(@CheckForNull Editor editor, MessagesConfiguration config, 055 @CheckForNull Runnable afterApply) { 056 closeDialog(null); 057 editPositionableFrame = new ConfigureMessages(editor, config, null, null, afterApply); 058 editPositionableFrame.initComponents(); 059 } 060 061 @CheckForNull 062 static ConfigureMessages getOpenDialog() { 063 if (editPositionableFrame != null && editPositionableFrame.isDisplayable()) { 064 return editPositionableFrame; 065 } 066 return null; 067 } 068 069 private static void closeDialog(@CheckForNull Editor editor) { 070 if (editPositionableFrame != null) { 071 editPositionableFrame.setVisible(false); 072 editPositionableFrame.dispose(); 073 074 if (editor != null) { 075 editor.setVisible(true); 076 } 077 } 078 } 079 080 private ConfigureMessages(@CheckForNull Editor editor, MessagesConfiguration config, 081 @CheckForNull MessagesPositionable newPositionable, @CheckForNull DoAfter doAfter, 082 @CheckForNull Runnable afterApply) { 083 super(Bundle.getMessage(newPositionable != null ? "AddMessagesPositionable" : "EditMessages"), false, false); 084 this.editor = editor; 085 this.messagesConfiguration = config; 086 this.newPositionable = newPositionable; 087 this.doAfter = doAfter; 088 this.afterApply = afterApply; 089 _foreground = config.getMessageForeground(); 090 _background = config.getMessageBackground(); 091 } 092 093 @Override 094 public void initComponents() { 095 addHelpMenu("html.hardware.dcc-ex.index_messages", true); 096 setLocation(50, 30); 097 Container contentPane = getContentPane(); 098 contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); 099 100 List<DCCppSystemConnectionMemo> systemConnections = 101 jmri.InstanceManager.getList(DCCppSystemConnectionMemo.class); 102 for (DCCppSystemConnectionMemo connection : systemConnections) { 103 DCCppConnection c = new DCCppConnection(connection); 104 _memoComboBox.addItem(c); 105 if (connection == messagesConfiguration.getMemo()) { 106 _memoComboBox.setSelectedItem(c); 107 } 108 } 109 if (_memoComboBox.getSelectedIndex() == -1 && _memoComboBox.getItemCount() > 0) { 110 _memoComboBox.setSelectedIndex(0); 111 } 112 _memoComboBox.setToolTipText(Bundle.getMessage("ConnectionHint")); 113 114 JPanel p = new JPanel(); 115 p.setLayout(new GridBagLayout()); 116 GridBagConstraints c = new GridBagConstraints(); 117 c.gridy = 0; 118 119 if (messagesConfiguration.isMemoEditable()) { 120 addRow(p, c, "Connection", _memoComboBox); 121 } 122 123 _expiryTextField.setText(Integer.toString(messagesConfiguration.getExpirySeconds())); 124 addRow(p, c, "ConfigureMessages_Expiry", _expiryTextField); 125 _maxLinesTextField.setText(Integer.toString(messagesConfiguration.getMaxLines())); 126 addRow(p, c, "ConfigureMessages_MaxLines", _maxLinesTextField); 127 128 if (editor == null) { 129 _autoOpenCheckBox.setSelected(messagesConfiguration.isAutoOpen()); 130 addRow(p, c, "ConfigureMessages_AutoOpen", _autoOpenCheckBox); 131 } 132 133 _playSoundCheckBox.setSelected(messagesConfiguration.isPlaySound()); 134 addRow(p, c, "ConfigureMessages_PlaySound", _playSoundCheckBox); 135 136 _soundFileTextField.setText(messagesConfiguration.getSoundFile()); 137 JButton chooseSound = new JButton(Bundle.getMessage("ConfigureMessages_Choose")); 138 chooseSound.addActionListener(e -> chooseSoundFile()); 139 JPanel soundPanel = new JPanel(); 140 soundPanel.add(_soundFileTextField); 141 soundPanel.add(chooseSound); 142 addRow(p, c, "ConfigureMessages_SoundFile", soundPanel); 143 144 if (editor != null) { 145 addRow(p, c, "ConfigureMessages_Foreground", colorButton(true)); 146 addRow(p, c, "ConfigureMessages_Background", colorButton(false)); 147 Dimension size = messagesConfiguration.getMessagesSize(); 148 if (size != null) { 149 _columnsTextField.setText(Integer.toString(size.width)); 150 _linesTextField.setText(Integer.toString(size.height)); 151 } 152 addRow(p, c, "ConfigureMessages_Columns", _columnsTextField); 153 addRow(p, c, "ConfigureMessages_Lines", _linesTextField); 154 c.gridx = 0; 155 c.gridwidth = 2; 156 c.anchor = GridBagConstraints.WEST; 157 p.add(new JLabel(Bundle.getMessage("ConfigureMessages_SizeHelp")), c); 158 } 159 160 contentPane.add(p); 161 162 JPanel buttons = new JPanel(); 163 buttons.setLayout(new WrapLayout()); 164 JButton cancel = new JButton(Bundle.getMessage("ButtonCancel")); 165 buttons.add(cancel); 166 cancel.addActionListener(e -> cancel()); 167 cancel.setToolTipText(Bundle.getMessage("CancelButtonHint")); 168 169 JButton ok = new JButton(Bundle.getMessage(newPositionable != null ? "ButtonCreate" : "ButtonOK")); 170 buttons.add(ok); 171 ok.addActionListener(e -> ok()); 172 contentPane.add(buttons); 173 174 addWindowListener(new java.awt.event.WindowAdapter() { 175 @Override 176 public void windowClosing(java.awt.event.WindowEvent e) { 177 cancel(); 178 } 179 }); 180 181 pack(); 182 setVisible(true); 183 } 184 185 void ok() { 186 try { 187 applyTo(messagesConfiguration); 188 if (afterApply != null) { 189 afterApply.run(); 190 } 191 if (newPositionable != null) { 192 addPositionable(); 193 } 194 closeDialog(editor); 195 } catch (NumberFormatException ex) { 196 JmriJOptionPane.showMessageDialog( 197 this, 198 Bundle.getMessage("ErrorMessagesMustBeInteger"), 199 Bundle.getMessage("ErrorTitle"), 200 JmriJOptionPane.ERROR_MESSAGE); 201 } 202 } 203 204 private void cancel() { 205 if (newPositionable != null) { 206 newPositionable.dispose(); 207 } 208 closeDialog(editor); 209 } 210 211 private void addPositionable() { 212 newPositionable.initComponents(); 213 newPositionable.setDisplayLevel(Editor.ICONS); 214 editor.setNextLocation(newPositionable); 215 try { 216 editor.putItem(newPositionable, true); 217 } catch (Positionable.DuplicateIdException e) { 218 // putItem with a null id never throws this 219 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 220 } 221 if (doAfter != null) { 222 doAfter.doAfter(newPositionable); 223 } 224 } 225 226 private void addRow(JPanel p, GridBagConstraints c, String labelKey, JComponent field) { 227 JLabel label = new JLabel(Bundle.getMessage(labelKey)); 228 label.setLabelFor(field); 229 c.gridwidth = 1; 230 c.gridx = 0; 231 c.anchor = GridBagConstraints.EAST; 232 c.weightx = 0; 233 p.add(label, c); 234 c.gridx = 1; 235 c.anchor = GridBagConstraints.WEST; 236 c.weightx = 1.0; 237 p.add(field, c); 238 c.gridy++; 239 } 240 241 private JButton colorButton(boolean foreground) { 242 JButton button = new JButton(Bundle.getMessage("ConfigureMessages_Choose")); 243 button.setOpaque(true); 244 button.setBackground(foreground ? _foreground : _background); 245 button.addActionListener(e -> { 246 String title = Bundle.getMessage(foreground ? "ConfigureMessages_Foreground" : "ConfigureMessages_Background"); 247 Color color = JmriColorChooser.showDialog(this, title, foreground ? _foreground : _background); 248 if (color != null) { 249 if (foreground) { 250 _foreground = color; 251 } else { 252 _background = color; 253 } 254 button.setBackground(color); 255 } 256 }); 257 return button; 258 } 259 260 private void chooseSoundFile() { 261 JFileChooser chooser = new JFileChooser(FileUtil.getExternalFilename("program:resources/sounds")); 262 if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { 263 File file = chooser.getSelectedFile(); 264 _soundFileTextField.setText(FileUtil.getPortableFilename(file)); 265 } 266 } 267 268 /** Write the dialog's values to config; throws NumberFormatException, without changing anything, on bad input. */ 269 void applyTo(MessagesConfiguration config) { 270 int expiry = Integer.parseInt(_expiryTextField.getText().trim()); 271 int maxLines = Integer.parseInt(_maxLinesTextField.getText().trim()); 272 Dimension size = null; 273 if (editor != null && !_columnsTextField.getText().isBlank() && !_linesTextField.getText().isBlank()) { 274 size = new Dimension( 275 Integer.parseInt(_columnsTextField.getText().trim()), 276 Integer.parseInt(_linesTextField.getText().trim())); 277 } 278 279 DCCppConnection connection = _memoComboBox.getItemAt(_memoComboBox.getSelectedIndex()); 280 if (config.isMemoEditable() && connection != null) { 281 config.setMemo(connection._memo); 282 } 283 config.setExpirySeconds(expiry); 284 config.setMaxLines(maxLines); 285 if (editor == null) { 286 config.setAutoOpen(_autoOpenCheckBox.isSelected()); 287 } else { 288 config.setMessageForeground(_foreground); 289 config.setMessageBackground(_background); 290 config.setMessagesSize(size); 291 } 292 config.setPlaySound(_playSoundCheckBox.isSelected()); 293 config.setSoundFile(_soundFileTextField.getText().trim()); 294 } 295 296 private static class DCCppConnection { 297 298 private final DCCppSystemConnectionMemo _memo; 299 300 public DCCppConnection(DCCppSystemConnectionMemo memo) { 301 _memo = memo; 302 } 303 304 @Override 305 public String toString() { 306 return _memo.getUserName(); 307 } 308 } 309 310 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ConfigureMessages.class); 311 312}