22
33package org .openlcb .swing ;
44
5+ import java .awt .FlowLayout ;
6+ import java .awt .Toolkit ;
7+ import java .awt .datatransfer .Clipboard ;
8+ import java .awt .datatransfer .ClipboardOwner ;
59import java .awt .datatransfer .DataFlavor ;
610import java .awt .datatransfer .StringSelection ;
711import java .awt .datatransfer .Transferable ;
812import java .awt .datatransfer .UnsupportedFlavorException ;
13+ import java .awt .event .ActionEvent ;
14+ import java .awt .event .ActionListener ;
15+ import java .awt .event .InputEvent ;
16+ import java .awt .event .MouseAdapter ;
17+ import java .awt .event .MouseEvent ;
18+ import java .awt .event .WindowAdapter ;
19+ import java .awt .event .WindowEvent ;
20+
921import java .io .IOException ;
1022import java .util .logging .Level ;
1123import java .util .logging .Logger ;
1224
25+ import javax .swing .JButton ;
1326import javax .swing .JComponent ;
27+ import javax .swing .JComboBox ;
28+ import javax .swing .JDialog ;
1429import javax .swing .JFormattedTextField ;
30+ import javax .swing .JFrame ;
31+ import javax .swing .JMenu ;
32+ import javax .swing .JMenuItem ;
33+ import javax .swing .JOptionPane ;
34+ import javax .swing .JPanel ;
35+ import javax .swing .JPopupMenu ;
36+ import javax .swing .JTextField ;
37+ import javax .swing .SwingUtilities ;
1538import javax .swing .TransferHandler ;
39+ import javax .swing .text .DefaultEditorKit ;
1640import javax .swing .text .JTextComponent ;
1741import javax .swing .text .MaskFormatter ;
1842
43+ import org .openlcb .EventID ;
44+
1945/**
2046 * Text field for entry of forced-valid EventID string.
2147 *
@@ -39,14 +65,236 @@ public static JFormattedTextField getEventIdTextField() {
3965 retval .setValue ("DD.DD.DD.DD.DD.DD.DD.DD" );
4066 retval .setPreferredSize (retval .getPreferredSize ());
4167 retval .setValue ("00.00.00.00.00.00.00.00" );
68+
4269 retval .setToolTipText ("EventID as eight-byte dotted-hex string, "
43- + "e.g. 01.02.0A.AB.34.56.78.00" );
70+ + "e.g. 01.02.0A.AB.34.56.78.00 "
71+ + "You can drag&drop or ctrl-click for more options" );
72+
4473 retval .setDragEnabled (true );
4574 retval .setTransferHandler (new CustomTransferHandler ());
4675
76+ configurePopUp (retval );
77+
4778 return retval ;
4879 }
4980
81+ private static void configurePopUp (JFormattedTextField textfield ) {
82+
83+ // JMRI's apps.Apps.java line 330 adds cut/copy/paste to all JTextComponents
84+ // (Also serves as example of cut/copy/paste code)
85+
86+ JPopupMenu popup = createPopupMenu (textfield );
87+ //textfield.add(popup);
88+ textfield .setComponentPopupMenu (popup );
89+ textfield .setInheritsPopupMenu (false );
90+ }
91+
92+ private static void checkAndShowPopup (JFormattedTextField textfield , MouseEvent event ) {
93+ if ( event .isPopupTrigger () ) {
94+ // user requested the popup menu
95+ JPopupMenu popup = createPopupMenu (textfield );
96+ popup .show (textfield , event .getX (), event .getY ());
97+ }
98+ }
99+
100+ private static JPopupMenu createPopupMenu (JFormattedTextField textfield ) {
101+ JPopupMenu popup = new JPopupMenu ();
102+
103+ // add the usual copy and paste operators
104+ JMenuItem menuItem = new JMenuItem ("Copy" );
105+ popup .add (menuItem );
106+ menuItem .addActionListener (new ActionListener () {
107+ @ Override
108+ public void actionPerformed (ActionEvent e ) {
109+ String s = textfield .getText ();
110+ SwingUtilities .invokeLater (new Runnable () {
111+ @ Override
112+ public void run () {
113+ textfield .selectAll ();
114+ }
115+ });
116+ StringSelection eventToCopy = new StringSelection (s );
117+ Clipboard clipboard = Toolkit .getDefaultToolkit ().getSystemClipboard ();
118+ clipboard .setContents (eventToCopy , new ClipboardOwner () {
119+ @ Override
120+ public void lostOwnership (Clipboard clipboard , Transferable transferable ) {
121+ }
122+ });
123+ }
124+ });
125+
126+ menuItem = new JMenuItem ("Paste" );
127+ popup .add (menuItem );
128+ menuItem .addActionListener (new ActionListener () {
129+ @ Override
130+ public void actionPerformed (ActionEvent e ) {
131+ Clipboard systemClipboard = Toolkit .getDefaultToolkit ().getSystemClipboard ();
132+ DataFlavor dataFlavor = DataFlavor .stringFlavor ;
133+
134+ Object text = null ;
135+ try {
136+ text = systemClipboard .getData (dataFlavor );
137+ } catch (UnsupportedFlavorException | IOException e1 ) {
138+ return ;
139+ }
140+ String pasteValue = (String ) text ;
141+ if (pasteValue != null ) {
142+ textfield .setText (pasteValue );
143+ }
144+ }
145+ });
146+
147+ // create a submenu for well-known events
148+ popup .add (makeWellKnownEventMenu (textfield ));
149+
150+ // Add the time events
151+ popup .add ( makeClockEventMenuItem (textfield ));
152+
153+ // Add the accessory decoder events
154+ popup .add (makeDccAccessoryEventMenuItem (textfield ));
155+
156+ // popup.add("Extended DCC accessory decoder events ...");
157+ // popup.add("DCC turnout feedback events ...");
158+ // popup.add("DCC system sensor feedback events ...");
159+
160+ return popup ;
161+ }
162+
163+ public static JMenu makeWellKnownEventMenu (JFormattedTextField textfield ) {
164+ JMenu wkeMenu = new JMenu ("Insert well-known event" );
165+ wkeMenu .add (new EventIdInserter (
166+ "Emergency off (de-energize)" , "01.00.00.00.05.00.FF.FF" , textfield ));
167+ wkeMenu .add (new EventIdInserter (
168+ "Clear emergency off (energize)" , "01.00.00.00.00.00.FF.FE" , textfield ));
169+
170+ wkeMenu .add (new EventIdInserter (
171+ "Emergency stop of all operations" , "01.00.00.00.05.00.FF.FD" , textfield ));
172+ wkeMenu .add (new EventIdInserter (
173+ "Clear emergency stop of all operations" ,"01.00.00.00.00.00.FF.FC" , textfield ));
174+
175+ wkeMenu .add (new EventIdInserter (
176+ "Start Default Fast Clock" , "01.01.00.00.01.00.F0.02" , textfield ));
177+ wkeMenu .add (new EventIdInserter (
178+ "Stop Default Fast Clock" , "01.01.00.00.01.00.F0.01" , textfield ));
179+
180+ return wkeMenu ;
181+ }
182+
183+ public static JMenuItem makeClockEventMenuItem (JFormattedTextField textfield ) {
184+ JMenuItem menuItem = new JMenuItem ("Insert Clock event..." );
185+ menuItem .addActionListener (new ActionListener () {
186+ @ Override
187+ public void actionPerformed (ActionEvent e ) {
188+ JDialog dialog = new JDialog ();
189+ dialog .setTitle ("Select Clock Settings" );
190+
191+ JPanel innerPanel = new JPanel (new FlowLayout ());
192+ JComboBox <String > clockBox = new JComboBox <String >(
193+ new String []{
194+ "Default Fast Clock" ,
195+ "Default Real-Time Clock" ,
196+ "Alternate Clock 1" ,
197+ "Alternate Clock 2"
198+ });
199+ innerPanel .add (clockBox );
200+ util .com .toedter .calendar .JHourMinuteChooser chooser = new util .com .toedter .calendar .JHourMinuteChooser ();
201+ innerPanel .add (chooser );
202+ JButton setButton = new JButton ("Set" );
203+ innerPanel .add (setButton );
204+ setButton .addActionListener (new ActionListener () {
205+ @ Override
206+ public void actionPerformed (ActionEvent e ) {
207+ String prefix = "01.01.00.00.01.0" +clockBox .getSelectedIndex ();
208+ int minute = Integer .parseInt (chooser .getMinute ());
209+ int hour = Integer .parseInt (chooser .getHour ());
210+ if (! chooser .getMeridian ().equals ("AM" )) hour = hour +12 ;
211+ textfield .setText (prefix +String .format (".%02X.%02X" , hour , minute ));
212+ dialog .dispatchEvent (new WindowEvent (dialog , WindowEvent .WINDOW_CLOSING ));
213+ }
214+ });
215+
216+ dialog .add (innerPanel );
217+ dialog .setModal (true );
218+ dialog .pack ();
219+ dialog .setDefaultCloseOperation (JFrame .DISPOSE_ON_CLOSE );
220+
221+ dialog .setVisible (true );
222+ }
223+ });
224+ return menuItem ;
225+ }
226+
227+ public static JMenuItem makeDccAccessoryEventMenuItem (JFormattedTextField textfield ) {
228+ JMenuItem menuItem = new JMenuItem ("Insert DCC accessory decoder events ..." );
229+ menuItem .addActionListener (new ActionListener () {
230+ @ Override
231+ public void actionPerformed (ActionEvent e ) {
232+ JDialog dialog = new JDialog ();
233+ dialog .setTitle ("Select DCC Accessory Decoder" );
234+
235+ JPanel innerPanel = new JPanel (new FlowLayout ());
236+
237+ JTextField number = new JTextField (12 );
238+ number .setText ("1" );
239+ innerPanel .add (number );
240+
241+ JComboBox <String > onOffBox = new JComboBox <String >(
242+ new String []{
243+ "Reversed/Inactive/Off" ,
244+ "Normal/Active/On"
245+ });
246+ innerPanel .add (onOffBox );
247+
248+ JButton setButton = new JButton ("Set" );
249+ innerPanel .add (setButton );
250+ setButton .addActionListener (new ActionListener () {
251+ @ Override
252+ public void actionPerformed (ActionEvent e ) {
253+ int from = Integer .parseInt (number .getText ().trim ());
254+
255+ // See JMRI OlcnAddress line 89 for Event ID coding
256+ int DD = (from -1 ) & 0x3 ;
257+ int aaaaaa = (( (from -1 ) >> 2 )+1 ) & 0x3F ;
258+ int AAA = ( (from ) >> 8 ) & 0x7 ;
259+ long event = 0x0101020000FF0000L | (AAA << 9 ) | (aaaaaa << 3 ) | (DD << 1 );
260+ event |= onOffBox .getSelectedIndex ();
261+
262+ EventID id = new EventID (String .format ("%016X" , event ));
263+ textfield .setText (id .toShortString ());
264+ dialog .dispatchEvent (new WindowEvent (dialog , WindowEvent .WINDOW_CLOSING ));
265+ }
266+ });
267+
268+ dialog .add (innerPanel );
269+ dialog .setModal (true );
270+ dialog .pack ();
271+ dialog .setDefaultCloseOperation (JFrame .DISPOSE_ON_CLOSE );
272+
273+ dialog .setVisible (true );
274+ }
275+ });
276+ return menuItem ;
277+ }
278+
279+ private static class EventIdInserter extends JMenuItem {
280+ public EventIdInserter (String name , String value , JFormattedTextField target ) {
281+ super (name );
282+ this .value = value ;
283+ this .target = target ;
284+
285+ addActionListener (new ActionListener () {
286+ @ Override
287+ public void actionPerformed (ActionEvent e ) {
288+ target .setText (value );
289+ }
290+ });
291+ }
292+ final String value ;
293+ final JFormattedTextField target ;
294+
295+ }
296+
297+
50298 private static MaskFormatter createFormatter (String s ) {
51299 MaskFormatter formatter = null ;
52300 try {
0 commit comments