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
@@ -267,10 +276,113 @@ protected void onCreateWindow(Container mainPane) {
267276 pane .add (serialRates );
268277
269278 mainPane .add (pane , BorderLayout .SOUTH );
279+
280+ textField = new JTextField (40 );
281+ // textField is selected every time the window is focused
282+ addWindowFocusListener (new WindowAdapter () {
283+ @ Override
284+ public void windowGainedFocus (WindowEvent e ) {
285+ textField .requestFocusInWindow ();
286+ }
287+ });
288+
289+ // Add cut/copy/paste contextual menu to the text input field.
290+ JPopupMenu menu = new JPopupMenu ();
291+
292+ Action cut = new DefaultEditorKit .CutAction ();
293+ cut .putValue (Action .NAME , tr ("Cut" ));
294+ menu .add (cut );
295+
296+ Action copy = new DefaultEditorKit .CopyAction ();
297+ copy .putValue (Action .NAME , tr ("Copy" ));
298+ menu .add (copy );
299+
300+ Action paste = new DefaultEditorKit .PasteAction ();
301+ paste .putValue (Action .NAME , tr ("Paste" ));
302+ menu .add (paste );
303+
304+ textField .setComponentPopupMenu (menu );
305+
306+ sendButton = new JButton (tr ("Send" ));
307+
308+ JPanel lowerPane = new JPanel ();
309+ lowerPane .setLayout (new BoxLayout (lowerPane , BoxLayout .X_AXIS ));
310+ lowerPane .setBorder (new EmptyBorder (4 , 4 , 4 , 4 ));
311+
312+ noLineEndingAlert = new JLabel (I18n .format (tr ("You've pressed {0} but nothing was sent. Should you select a line ending?" ), tr ("Send" )));
313+ noLineEndingAlert .setToolTipText (noLineEndingAlert .getText ());
314+ noLineEndingAlert .setForeground (pane .getBackground ());
315+ Dimension minimumSize = new Dimension (noLineEndingAlert .getMinimumSize ());
316+ minimumSize .setSize (minimumSize .getWidth () / 3 , minimumSize .getHeight ());
317+ noLineEndingAlert .setMinimumSize (minimumSize );
318+
319+
320+ lineEndings = new JComboBox <String >(new String []{tr ("No line ending" ), tr ("Newline" ), tr ("Carriage return" ), tr ("Both NL & CR" )});
321+ lineEndings .addActionListener ((ActionEvent event ) -> {
322+ PreferencesData .setInteger ("serial.line_ending" , lineEndings .getSelectedIndex ());
323+ noLineEndingAlert .setForeground (pane .getBackground ());
324+ });
325+ lineEndings .setMaximumSize (lineEndings .getMinimumSize ());
326+
327+ lowerPane .add (textField );
328+ lowerPane .add (Box .createRigidArea (new Dimension (4 , 0 )));
329+ lowerPane .add (sendButton );
330+
331+ pane .add (lowerPane );
332+ pane .add (noLineEndingAlert );
333+ pane .add (Box .createRigidArea (new Dimension (8 , 0 )));
334+ pane .add (lineEndings );
335+
336+ applyPreferences ();
337+
338+ onSendCommand ((ActionEvent event ) -> {
339+ send (textField .getText ());
340+ textField .setText ("" );
341+ });
342+
343+ }
344+
345+ private void send (String string ) {
346+ String s = string ;
347+ if (serial != null ) {
348+ switch (lineEndings .getSelectedIndex ()) {
349+ case 1 :
350+ s += "\n " ;
351+ break ;
352+ case 2 :
353+ s += "\r " ;
354+ break ;
355+ case 3 :
356+ s += "\r \n " ;
357+ break ;
358+ default :
359+ break ;
360+ }
361+ if ("" .equals (s ) && lineEndings .getSelectedIndex () == 0 && !PreferencesData .has ("runtime.line.ending.alert.notified" )) {
362+ noLineEndingAlert .setForeground (Color .RED );
363+ PreferencesData .set ("runtime.line.ending.alert.notified" , "true" );
364+ }
365+ serial .write (s );
366+ }
367+ }
368+
369+ public void onSendCommand (ActionListener listener ) {
370+ textField .addActionListener (listener );
371+ sendButton .addActionListener (listener );
372+ }
373+
374+ public void appyPreferences () {
375+ // Apply line endings.
376+ if (PreferencesData .get ("serial.line_ending" ) != null ) {
377+ lineEndings .setSelectedIndex (PreferencesData .getInteger ("serial.line_ending" ));
378+ }
270379 }
271380
272381 protected void onEnableWindow (boolean enable ) {
273382 serialRates .setEnabled (enable );
383+ textField .setEnabled (enable );
384+ sendButton .setEnabled (enable );
385+ lineEndings .setEnabled (enable );
274386 }
275387
276388 private void onSerialRateChange (ActionListener listener ) {
0 commit comments