1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package net.sourceforge.jarduino.gui;
18
19 import java.awt.Color;
20 import java.awt.Component;
21 import java.awt.event.FocusEvent;
22 import java.awt.event.FocusListener;
23 import java.text.NumberFormat;
24 import java.text.ParseException;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import javax.swing.AbstractCellEditor;
30 import javax.swing.JComponent;
31 import javax.swing.JTable;
32 import javax.swing.JTextField;
33 import javax.swing.RowFilter;
34 import javax.swing.SwingConstants;
35 import javax.swing.border.Border;
36 import javax.swing.border.LineBorder;
37 import javax.swing.table.AbstractTableModel;
38 import javax.swing.table.DefaultTableCellRenderer;
39 import javax.swing.table.TableCellEditor;
40 import javax.swing.table.TableCellRenderer;
41 import javax.swing.table.TableColumn;
42 import javax.swing.table.TableColumnModel;
43 import javax.swing.table.TableRowSorter;
44
45 import net.sourceforge.jarduino.message.ArduinoMessage;
46 import net.sourceforge.jarduino.message.ArduinoSignal;
47 import net.sourceforge.jarduino.message.ArduinoSignalDefinition;
48 import net.sourceforge.jarduino.message.ArduinoSignalFactor;
49 import net.sourceforge.jarduino.message.ArduinoSignalRange;
50 import net.sourceforge.jarduino.util.ArduinoField;
51
52
53
54
55 public class ArduinoTableSignal {
56
57
58
59 private static final Color[] COLORS = {
60 Color.decode("#FF0000"), Color.decode("#FF7F00"), Color.decode("#FFD400"), Color.decode("#FFFF00"),
61 Color.decode("#BFFF00"), Color.decode("#6AFF00"), Color.decode("#00EAFF"), Color.decode("#0095FF"),
62 Color.decode("#0040FF"), Color.decode("#AA00FF"), Color.decode("#FF00AA"), Color.decode("#EDB9B9"),
63 Color.decode("#E7E9B9"), Color.decode("#B9EDE0"), Color.decode("#B9D7ED"), Color.decode("#DCB9ED"),
64 Color.decode("#8F2323"), Color.decode("#8F6A23"), Color.decode("#4F8F23"), Color.decode("#23628F"),
65 Color.decode("#6B238F"), Color.decode("#000000"), Color.decode("#737373"), Color.decode("#CCCCCC")
66 };
67
68
69
70
71 private static final Border BORDER_NORMAL = new LineBorder(Color.black);
72
73
74
75
76 private static final Border BORDER_ERROR = new LineBorder(Color.red);
77
78
79
80
81 private static final double ROUNDING = 0.5;
82
83
84
85
86 private static final int WIDTH_SMALL = 20;
87
88
89
90
91 private static final int WIDTH_MEDIUM = 40;
92
93
94
95
96 private static final int WIDTH_LARGE = 150;
97
98
99
100
101 private final ArduinoPanelSignal theOwner;
102
103
104
105
106 private final JTable theTable;
107
108
109
110
111 private final List<ArduinoSignal> theSignals;
112
113
114
115
116 private final List<byte[]> theBitMaps;
117
118
119
120
121 private final Map<ArduinoSignal, Object> theValues;
122
123
124
125
126 private final Map<ArduinoSignal, Color> theColours;
127
128
129
130
131 private final ArduinoSignalModel theModel;
132
133
134
135
136 private final TableColumn theMultiCol;
137
138
139
140
141 private boolean multiVisible;
142
143
144
145
146 private ArduinoMessage theMessage;
147
148
149
150
151
152
153 ArduinoTableSignal(final ArduinoPanelSignal pOwner) {
154
155 theOwner = pOwner;
156
157
158 theTable = new JTable();
159
160
161 theSignals = new ArrayList<>();
162 theBitMaps = new ArrayList<>();
163 theValues = new HashMap<>();
164 theColours = new HashMap<>();
165
166
167 theModel = new ArduinoSignalModel();
168 theTable.setModel(theModel);
169
170
171 final TableRowSorter<ArduinoSignalModel> mySorter = new TableRowSorter<>(theModel);
172 mySorter.setRowFilter(new MultiFilter());
173 theTable.setRowSorter(mySorter);
174
175
176 theMultiCol = configureColumns();
177 multiVisible = true;
178 }
179
180
181
182
183
184 private TableColumn configureColumns() {
185
186 final TableCellRenderer myRightRenderer = new AlignmentRenderer(SwingConstants.RIGHT);
187 final TableCellRenderer myCenterRenderer = new AlignmentRenderer(SwingConstants.CENTER);
188 final TableCellRenderer myColorRenderer = new ColorRenderer();
189 final TableCellEditor myCellEditor = new DataCellEditor();
190
191
192 final TableColumnModel myModel = theTable.getColumnModel();
193
194
195 TableColumn myColumn = myModel.getColumn(ArduinoSignalModel.COL_SIGNAL);
196 myColumn.setPreferredWidth(WIDTH_LARGE);
197 myColumn.setCellRenderer(myRightRenderer);
198
199
200 myColumn = myModel.getColumn(ArduinoSignalModel.COL_VALUE);
201 myColumn.setPreferredWidth(WIDTH_MEDIUM);
202 myColumn.setCellRenderer(myCenterRenderer);
203 myColumn.setCellEditor(myCellEditor);
204
205
206 myColumn = myModel.getColumn(ArduinoSignalModel.COL_COLOR);
207 myColumn.setPreferredWidth(WIDTH_MEDIUM);
208 myColumn.setMaxWidth(WIDTH_MEDIUM);
209 myColumn.setCellRenderer(myColorRenderer);
210
211
212 myColumn = myModel.getColumn(ArduinoSignalModel.COL_START);
213 myColumn.setPreferredWidth(WIDTH_SMALL);
214 myColumn.setMaxWidth(WIDTH_SMALL);
215 myColumn.setCellRenderer(myCenterRenderer);
216
217
218 myColumn = myModel.getColumn(ArduinoSignalModel.COL_LENGTH);
219 myColumn.setPreferredWidth(WIDTH_SMALL);
220 myColumn.setMaxWidth(WIDTH_SMALL);
221 myColumn.setCellRenderer(myCenterRenderer);
222
223
224 myColumn = myModel.getColumn(ArduinoSignalModel.COL_ENDIAN);
225 myColumn.setPreferredWidth(WIDTH_SMALL);
226 myColumn.setMaxWidth(WIDTH_SMALL);
227 myColumn.setCellRenderer(myCenterRenderer);
228
229
230 myColumn = myModel.getColumn(ArduinoSignalModel.COL_UNITS);
231 myColumn.setPreferredWidth(WIDTH_MEDIUM);
232 myColumn.setCellRenderer(myCenterRenderer);
233
234
235 myColumn = myModel.getColumn(ArduinoSignalModel.COL_FACTOR);
236 myColumn.setPreferredWidth(WIDTH_MEDIUM);
237 myColumn.setCellRenderer(myCenterRenderer);
238
239
240 myColumn = myModel.getColumn(ArduinoSignalModel.COL_OFFSET);
241 myColumn.setPreferredWidth(WIDTH_MEDIUM);
242 myColumn.setCellRenderer(myCenterRenderer);
243
244
245 myColumn = myModel.getColumn(ArduinoSignalModel.COL_MIN);
246 myColumn.setPreferredWidth(WIDTH_MEDIUM);
247 myColumn.setCellRenderer(myCenterRenderer);
248
249
250 myColumn = myModel.getColumn(ArduinoSignalModel.COL_MAX);
251 myColumn.setPreferredWidth(WIDTH_MEDIUM);
252 myColumn.setCellRenderer(myCenterRenderer);
253
254
255 myColumn = myModel.getColumn(ArduinoSignalModel.COL_MULTI);
256 myColumn.setPreferredWidth(WIDTH_SMALL);
257 myColumn.setCellRenderer(myCenterRenderer);
258 return myColumn;
259 }
260
261
262
263
264
265
266 JComponent getComponent() {
267 return theTable;
268 }
269
270
271
272
273
274
275
276 private static Color getColourForIndex(final int pIndex) {
277 return COLORS[pIndex % COLORS.length];
278 }
279
280
281
282
283
284
285 void configureTable(final ArduinoMessage pMessage) {
286
287 theMessage = pMessage;
288
289
290 theModel.refresh();
291
292
293 final boolean bMultiVisible = theMessage.hasMultiplex();
294
295
296 if (bMultiVisible != multiVisible) {
297
298 final TableColumnModel myColModel = theTable.getColumnModel();
299
300
301 if (multiVisible) {
302 myColModel.removeColumn(theMultiCol);
303
304
305 } else {
306 myColModel.addColumn(theMultiCol);
307 myColModel.moveColumn(ArduinoSignalModel.COL_MAX, ArduinoSignalModel.COL_MULTI);
308 }
309
310
311 multiVisible = !multiVisible;
312 }
313 }
314
315
316
317
318
319
320 int getNumRows() {
321 return theSignals.size();
322 }
323
324
325
326
327
328
329
330 Color getColorForRow(final int pRowIndex) {
331 final ArduinoSignal mySignal = theSignals.get(pRowIndex);
332 return theColours.get(mySignal);
333 }
334
335
336
337
338
339
340
341 byte[] getBitMapForRow(final int pRowIndex) {
342 return theBitMaps.get(pRowIndex);
343 }
344
345
346
347
348 void loadValues() {
349
350 theValues.clear();
351 theColours.clear();
352
353
354 Long myMultiplex = ArduinoSignal.MULTI_NONE;
355 int myColorId = 0;
356 for (ArduinoSignal mySignal : theSignals) {
357
358 final Number myMId = mySignal.getMultiplexId();
359 if (myMId.equals(ArduinoSignal.MULTI_NONE)
360 || myMId.equals(myMultiplex)) {
361
362 final Number myValue = getValueForSignal(mySignal);
363 theValues.put(mySignal, myValue);
364
365
366 theColours.put(mySignal, getColourForIndex(myColorId++));
367
368
369 if (mySignal.isMultiplex()) {
370 myMultiplex = myValue.longValue();
371 }
372 }
373 }
374
375
376 if (theModel != null) {
377 theModel.fireTableDataChanged();
378 }
379 }
380
381
382
383
384
385
386
387 private Number getValueForSignal(final ArduinoSignal pSignal) {
388
389 final byte[] myData = theOwner.getData();
390
391
392 final ArduinoSignalDefinition myDef = pSignal.getDefinition();
393 final long myValue = myDef.getDataType().isLittleEndian()
394 ? ArduinoField.parseLEField(myData, myDef.getStartBit(), myDef.getBitLength(), myDef.getDataType().isSigned())
395 : ArduinoField.parseBEField(myData, myDef.getStartBit(), myDef.getBitLength(), myDef.getDataType().isSigned());
396
397
398 final ArduinoSignalFactor myFactor = pSignal.getFactor();
399 return myFactor.isFloat()
400 ? (Number) (myFactor.getFactor().doubleValue() * myValue + myFactor.getOffset().doubleValue())
401 : (Number) (myFactor.getFactor().longValue() * myValue + myFactor.getOffset().longValue());
402 }
403
404
405
406
407 private class ArduinoSignalModel
408 extends AbstractTableModel {
409
410
411
412 private static final long serialVersionUID = 3030910683228702589L;
413
414
415
416
417 private static final int COL_SIGNAL = 0;
418
419
420
421
422 private static final int COL_VALUE = COL_SIGNAL + 1;
423
424
425
426
427 private static final int COL_START = COL_VALUE + 1;
428
429
430
431
432 private static final int COL_LENGTH = COL_START + 1;
433
434
435
436
437 private static final int COL_ENDIAN = COL_LENGTH + 1;
438
439
440
441
442 private static final int COL_MULTI = COL_ENDIAN + 1;
443
444
445
446
447 private static final int COL_COLOR = COL_MULTI + 1;
448
449
450
451
452 private static final int COL_UNITS = COL_COLOR + 1;
453
454
455
456
457 private static final int COL_FACTOR = COL_UNITS + 1;
458
459
460
461
462 private static final int COL_OFFSET = COL_FACTOR + 1;
463
464
465
466
467 private static final int COL_MIN = COL_OFFSET + 1;
468
469
470
471
472 private static final int COL_MAX = COL_MIN + 1;
473
474
475
476
477 ArduinoSignalModel() {
478 }
479
480
481
482
483 void refresh() {
484
485 theSignals.clear();
486
487
488 theSignals.addAll(theMessage.getAllSignals());
489
490
491 loadValues();
492 loadBitMaps();
493
494
495 fireTableDataChanged();
496 }
497
498
499
500
501 private void loadBitMaps() {
502
503 theBitMaps.clear();
504
505
506 for (ArduinoSignal mySignal : theSignals) {
507
508 final byte[] myBitMap = getBitMapForSignal(mySignal);
509 theBitMaps.add(myBitMap);
510 }
511 }
512
513 @Override
514 public int getRowCount() {
515 return theSignals.size();
516 }
517
518 @Override
519 public String getColumnName(final int pColIndex) {
520 switch (pColIndex) {
521 case COL_SIGNAL:
522 return "Signal";
523 case COL_VALUE:
524 return "Value";
525 case COL_START:
526 return "S";
527 case COL_LENGTH:
528 return "L";
529 case COL_ENDIAN:
530 return "E";
531 case COL_MULTI:
532 return "M";
533 case COL_COLOR:
534 return "Color";
535 case COL_UNITS:
536 return "Units";
537 case COL_FACTOR:
538 return "Factor";
539 case COL_OFFSET:
540 return "Offset";
541 case COL_MIN:
542 return "Min";
543 case COL_MAX:
544 return "Max";
545 default:
546 return null;
547 }
548 }
549
550 @Override
551 public int getColumnCount() {
552 return COL_MAX + 1;
553 }
554
555 @Override
556 public boolean isCellEditable(final int pRowIndex,
557 final int pColIndex) {
558 final ArduinoSignal mySignal = theSignals.get(pRowIndex);
559 return pColIndex == COL_VALUE && theValues.get(mySignal) != null;
560 }
561
562 @Override
563 public Object getValueAt(final int pRowIndex,
564 final int pColIndex) {
565 final ArduinoSignal mySignal = theSignals.get(pRowIndex);
566 final ArduinoSignalDefinition myDef = mySignal.getDefinition();
567 final ArduinoSignalFactor myFactor = mySignal.getFactor();
568 final ArduinoSignalRange myRange = mySignal.getRange();
569 final long myMId = mySignal.getMultiplexId();
570 switch (pColIndex) {
571 case COL_SIGNAL:
572 return mySignal.getName();
573 case COL_VALUE:
574 return theValues.get(mySignal);
575 case COL_START:
576 return myDef.getStartBit();
577 case COL_LENGTH:
578 return myDef.getBitLength();
579 case COL_ENDIAN:
580 return myDef.getDataType().isLittleEndian()
581 ? "L"
582 : "B";
583 case COL_MULTI:
584 if (mySignal.isMultiplex()) {
585 return "M";
586 }
587 return myMId == -1
588 ? null
589 : myMId;
590 case COL_COLOR:
591 return theColours.get(mySignal);
592 case COL_UNITS:
593 return mySignal.getUnits();
594 case COL_FACTOR:
595 return myFactor.getFactor();
596 case COL_OFFSET:
597 return myFactor.getOffset();
598 case COL_MIN:
599 return myRange.unBounded()
600 ? null
601 : myRange.getMinimum();
602 case COL_MAX:
603 return myRange.unBounded()
604 ? null
605 : myRange.getMaximum();
606 default:
607 return null;
608 }
609 }
610
611 @Override
612 public void setValueAt(final Object pValue,
613 final int pRowIndex,
614 final int pColIndex) {
615
616 final ArduinoSignal mySignal = theSignals.get(pRowIndex);
617 setValueForSignal(mySignal, (Number) pValue);
618
619
620 loadValues();
621
622
623 theOwner.updateHexAndBinary();
624 theOwner.updateBitColors();
625 }
626
627
628
629
630
631
632
633 private byte[] getBitMapForSignal(final ArduinoSignal pSignal) {
634
635 final byte[] myData = new byte[theMessage.getLength()];
636
637
638 final ArduinoSignalDefinition myDef = pSignal.getDefinition();
639 if (myDef.getDataType().isLittleEndian()) {
640 ArduinoField.setLEFieldBits(myData, myDef.getStartBit(), myDef.getBitLength());
641 } else {
642 ArduinoField.setBEFieldBits(myData, myDef.getStartBit(), myDef.getBitLength());
643 }
644
645
646 return myData;
647 }
648
649
650
651
652
653
654
655 private void setValueForSignal(final ArduinoSignal pSignal,
656 final Number pValue) {
657
658 final byte[] myData = theOwner.getData();
659
660
661 final ArduinoSignalFactor myFactor = pSignal.getFactor();
662 final long myValue = myFactor.isFloat()
663 ? (long) (ROUNDING + ((pValue.doubleValue() - myFactor.getOffset().doubleValue()) / myFactor.getFactor().doubleValue()))
664 : (pValue.longValue() - myFactor.getOffset().longValue()) / myFactor.getFactor().longValue();
665
666
667 final ArduinoSignalDefinition myDef = pSignal.getDefinition();
668 if (myDef.getDataType().isLittleEndian()) {
669 ArduinoField.setLEField(myData, myDef.getStartBit(), myDef.getBitLength(), myValue);
670 } else {
671 ArduinoField.setBEField(myData, myDef.getStartBit(), myDef.getBitLength(), myValue);
672 }
673 }
674 }
675
676
677
678
679 private static class ColorRenderer extends DefaultTableCellRenderer {
680 @Override
681 public void setValue(final Object pValue) {
682 setBackground((Color) pValue);
683 }
684 }
685
686
687
688
689 private class AlignmentRenderer extends DefaultTableCellRenderer {
690
691
692
693 private static final long serialVersionUID = -6387123991399458414L;
694
695
696
697
698 private final NumberFormat myFormatter = NumberFormat.getInstance();
699
700
701
702
703
704
705 AlignmentRenderer(final int pAlignment) {
706 setHorizontalAlignment(pAlignment);
707 }
708
709 @Override
710 public JComponent getTableCellRendererComponent(final JTable pTable,
711 final Object pValue,
712 final boolean pSelected,
713 final boolean hasFocus,
714 final int pRow,
715 final int pCol) {
716
717 final int myRow = pTable.convertRowIndexToModel(pRow);
718 final ArduinoSignal mySignal = theSignals.get(myRow);
719
720
721 String myValue = pValue == null
722 ? null
723 : pValue.toString();
724
725
726 if (pValue instanceof Double) {
727
728 final int myNumDecimals = mySignal.getFactor().getNumDecimals();
729 myFormatter.setMaximumFractionDigits(myNumDecimals);
730 myFormatter.setMinimumFractionDigits(myNumDecimals);
731
732
733 myValue = myFormatter.format(((Double) pValue).doubleValue());
734 }
735
736
737 setText(myValue);
738
739
740 return this;
741 }
742 }
743
744
745
746
747 private class DataCellEditor
748 extends AbstractCellEditor
749 implements TableCellEditor {
750
751
752
753 private static final long serialVersionUID = 7142514324836088035L;
754
755
756
757
758 private final JTextField theEditor;
759
760
761
762
763 private final NumberFormat myFormatter = NumberFormat.getInstance();
764
765
766
767
768 private transient ArduinoSignal theSignal;
769
770
771
772
773 private Number theValue;
774
775
776
777
778 private boolean stoppingEdit;
779
780
781
782
783 DataCellEditor() {
784 theEditor = new JTextField();
785 theEditor.setHorizontalAlignment(SwingConstants.CENTER);
786 theEditor.addFocusListener(new FocusListener() {
787 @Override
788 public void focusLost(final FocusEvent pE) {
789
790 }
791 @Override
792 public void focusGained(final FocusEvent pE) {
793 theEditor.selectAll();
794 }
795 });
796 }
797
798 @Override
799 public Number getCellEditorValue() {
800 return theValue;
801 }
802
803 @Override
804 public Component getTableCellEditorComponent(final JTable pTable,
805 final Object pValue,
806 final boolean pSelected,
807 final int pRow,
808 final int pCol) {
809
810 final int myRow = pTable.convertRowIndexToModel(pRow);
811 theSignal = theSignals.get(myRow);
812
813
814 String myValue = null;
815 if (pValue instanceof Double) {
816
817 final int myNumDecimals = theSignal.getFactor().getNumDecimals();
818 myFormatter.setMaximumFractionDigits(myNumDecimals);
819 myFormatter.setMinimumFractionDigits(myNumDecimals);
820 myValue = myFormatter.format(((Number) pValue).doubleValue());
821 } else if (pValue instanceof Long) {
822 myValue = pValue.toString();
823 }
824
825
826 theEditor.setText(myValue);
827 theEditor.setBorder(BORDER_NORMAL);
828
829
830 return theEditor;
831 }
832
833 @Override
834 public boolean stopCellEditing() {
835
836 if (stoppingEdit) {
837 return true;
838 }
839
840
841 stoppingEdit = true;
842 try {
843 theValue = myFormatter.parse(theEditor.getText());
844
845
846 } catch (ParseException e) {
847
848 stoppingEdit = false;
849 theEditor.setBorder(BORDER_ERROR);
850 return false;
851 }
852
853
854 if (theValue instanceof Long && theSignal.isFloat()) {
855 theValue = theValue.doubleValue();
856 }
857 if (theValue instanceof Double && !theSignal.isFloat()) {
858 theValue = theValue.longValue();
859 }
860
861
862 boolean bComplete = false;
863 if (checkValueForSignal(theSignal, theValue)) {
864
865 bComplete = super.stopCellEditing();
866
867
868 } else {
869 theEditor.setBorder(BORDER_ERROR);
870 }
871
872
873 stoppingEdit = false;
874 return bComplete;
875 }
876
877
878
879
880
881
882
883
884 private boolean checkValueForSignal(final ArduinoSignal pSignal,
885 final Number pValue) {
886
887 final ArduinoSignalRange myRange = pSignal.getRange();
888 if (myRange.unBounded()) {
889 return true;
890 }
891
892
893 return pValue instanceof Double
894 ? pValue.doubleValue() >= myRange.getMinimum().doubleValue()
895 && pValue.doubleValue() <= myRange.getMaximum().doubleValue()
896 : pValue.longValue() >= myRange.getMinimum().longValue()
897 && pValue.longValue() <= myRange.getMaximum().longValue();
898 }
899 }
900
901
902
903
904 private class MultiFilter extends RowFilter<ArduinoSignalModel, Integer> {
905 @Override
906 public boolean include(final Entry<? extends ArduinoSignalModel, ? extends Integer> entry) {
907
908 final ArduinoSignal myMulti = theMessage.getMultiplexSignal();
909 if (myMulti == null) {
910 return true;
911 }
912
913
914 final ArduinoSignal mySignal = theSignals.get(entry.getIdentifier());
915 final Long myMId = mySignal.getMultiplexId();
916 return myMId.equals(ArduinoSignal.MULTI_NONE)
917 || myMId.equals(theValues.get(myMulti));
918 }
919 }
920 }