2626import java .util .ArrayList ;
2727import javax .swing .*;
2828import javax .swing .border .EmptyBorder ;
29+ import javax .swing .text .DefaultEditorKit ;
2930import java .awt .*;
31+ import java .awt .event .ActionEvent ;
3032import java .awt .event .ActionListener ;
33+ import java .awt .event .WindowAdapter ;
34+ import java .awt .event .WindowEvent ;
3135import java .awt .geom .AffineTransform ;
3236import java .awt .geom .Rectangle2D ;
3337
@@ -40,6 +44,11 @@ public class SerialPlotter extends AbstractMonitor {
4044 private Serial serial ;
4145 private int serialRate , xCount ;
4246
47+ private JLabel noLineEndingAlert ;
48+ private JTextField textField ;
49+ private JButton sendButton ;
50+ private JComboBox <String > lineEndings ;
51+
4352 private ArrayList <Graph > graphs ;
4453 private final static int BUFFER_CAPACITY = 500 ;
4554
@@ -254,10 +263,112 @@ protected void onCreateWindow(Container mainPane) {
254263 pane .add (serialRates );
255264
256265 mainPane .add (pane , BorderLayout .SOUTH );
266+
267+ textField = new JTextField (40 );
268+ // textField is selected every time the window is focused
269+ addWindowFocusListener (new WindowAdapter () {
270+ @ Override
271+ public void windowGainedFocus (WindowEvent e ) {
272+ textField .requestFocusInWindow ();
273+ }
274+ });
275+
276+ // Add cut/copy/paste contextual menu to the text input field.
277+ JPopupMenu menu = new JPopupMenu ();
278+
279+ Action cut = new DefaultEditorKit .CutAction ();
280+ cut .putValue (Action .NAME , tr ("Cut" ));
281+ menu .add (cut );
282+
283+ Action copy = new DefaultEditorKit .CopyAction ();
284+ copy .putValue (Action .NAME , tr ("Copy" ));
285+ menu .add (copy );
286+
287+ Action paste = new DefaultEditorKit .PasteAction ();
288+ paste .putValue (Action .NAME , tr ("Paste" ));
289+ menu .add (paste );
290+
291+ textField .setComponentPopupMenu (menu );
292+
293+ sendButton = new JButton (tr ("Send" ));
294+
295+ JPanel lowerPane = new JPanel ();
296+ lowerPane .setLayout (new BoxLayout (lowerPane , BoxLayout .X_AXIS ));
297+ lowerPane .setBorder (new EmptyBorder (4 , 4 , 4 , 4 ));
298+
299+ noLineEndingAlert = new JLabel (I18n .format (tr ("You've pressed {0} but nothing was sent. Should you select a line ending?" ), tr ("Send" )));
300+ noLineEndingAlert .setToolTipText (noLineEndingAlert .getText ());
301+ noLineEndingAlert .setForeground (pane .getBackground ());
302+ Dimension minimumSize = new Dimension (noLineEndingAlert .getMinimumSize ());
303+ minimumSize .setSize (minimumSize .getWidth () / 3 , minimumSize .getHeight ());
304+ noLineEndingAlert .setMinimumSize (minimumSize );
305+
306+
307+ lineEndings = new JComboBox <String >(new String []{tr ("No line ending" ), tr ("Newline" ), tr ("Carriage return" ), tr ("Both NL & CR" )});
308+ lineEndings .addActionListener ((ActionEvent event ) -> {
309+ PreferencesData .setInteger ("serial.line_ending" , lineEndings .getSelectedIndex ());
310+ noLineEndingAlert .setForeground (pane .getBackground ());
311+ });
312+ lineEndings .setMaximumSize (lineEndings .getMinimumSize ());
313+
314+ lowerPane .add (textField );
315+ lowerPane .add (Box .createRigidArea (new Dimension (4 , 0 )));
316+ lowerPane .add (sendButton );
317+
318+ pane .add (lowerPane );
319+ pane .add (noLineEndingAlert );
320+ pane .add (Box .createRigidArea (new Dimension (8 , 0 )));
321+ pane .add (lineEndings );
322+
323+ applyPreferences ();
324+
325+ onSendCommand ((ActionEvent event ) -> {
326+ send (textField .getText ());
327+ textField .setText ("" );
328+ });
329+
330+ }
331+
332+ private void send (String s ) {
333+ if (serial != null ) {
334+ switch (lineEndings .getSelectedIndex ()) {
335+ case 1 :
336+ s += "\n " ;
337+ break ;
338+ case 2 :
339+ s += "\r " ;
340+ break ;
341+ case 3 :
342+ s += "\r \n " ;
343+ break ;
344+ default :
345+ break ;
346+ }
347+ if ("" .equals (s ) && lineEndings .getSelectedIndex () == 0 && !PreferencesData .has ("runtime.line.ending.alert.notified" )) {
348+ noLineEndingAlert .setForeground (Color .RED );
349+ PreferencesData .set ("runtime.line.ending.alert.notified" , "true" );
350+ }
351+ serial .write (s );
352+ }
353+ }
354+
355+ public void onSendCommand (ActionListener listener ) {
356+ textField .addActionListener (listener );
357+ sendButton .addActionListener (listener );
358+ }
359+
360+ public void appyPreferences () {
361+ // Apply line endings.
362+ if (PreferencesData .get ("serial.line_ending" ) != null ) {
363+ lineEndings .setSelectedIndex (PreferencesData .getInteger ("serial.line_ending" ));
364+ }
257365 }
258366
259367 protected void onEnableWindow (boolean enable ) {
260368 serialRates .setEnabled (enable );
369+ textField .setEnabled (enable );
370+ sendButton .setEnabled (enable );
371+ lineEndings .setEnabled (enable );
261372 }
262373
263374 private void onSerialRateChange (ActionListener listener ) {
0 commit comments