View Javadoc
1   /*******************************************************************************
2    * jArduino: Arduino C++ Code Generation From Java
3    * Copyright 2020 Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   ******************************************************************************/
17  package net.sourceforge.jarduino.gui;
18  
19  import java.awt.BorderLayout;
20  import java.awt.Color;
21  import java.awt.Dimension;
22  import java.awt.Font;
23  import java.awt.GridBagConstraints;
24  import java.awt.GridBagLayout;
25  import java.awt.event.MouseAdapter;
26  import java.awt.event.MouseEvent;
27  import javax.swing.BorderFactory;
28  import javax.swing.Box;
29  import javax.swing.BoxLayout;
30  import javax.swing.JComponent;
31  import javax.swing.JFrame;
32  import javax.swing.JLabel;
33  import javax.swing.JPanel;
34  import javax.swing.JScrollPane;
35  import javax.swing.SwingConstants;
36  import javax.swing.border.Border;
37  
38  import net.sourceforge.jarduino.message.ArduinoMessage;
39  import net.sourceforge.jarduino.message.ArduinoSystem;
40  import net.sourceforge.jarduino.util.ArduinoHex;
41  
42  /**
43   * Message Signals display panel.
44   */
45  public class ArduinoPanelSignal {
46      /**
47       * Font.
48       */
49      static final Font FONT = new Font("monospaced", Font.BOLD, 16);
50  
51      /**
52       * Font.
53       */
54      static final Font FONTX = new Font("SansSerif", Font.BOLD, 16);
55  
56      /**
57       * The Panel.
58       */
59      private final JPanel thePanel;
60  
61      /**
62       * The Message table.
63       */
64      private final ArduinoTableSignal theTable;
65  
66      /**
67       * The select button.
68       */
69      private final ArduinoScrollButton<ArduinoMessage> theSelect;
70  
71      /**
72       * The Hex Data.
73       */
74      private final JLabel theHex;
75  
76      /**
77       * The MsgId.
78       */
79      private final JLabel theMsgId;
80  
81      /**
82       * The Binary Data.
83       */
84      private final BinaryData theBinary;
85  
86      /**
87       * The message data.
88       */
89      private byte[] theData;
90  
91      /**
92       * Constructor.
93       * @param pFrame the frame
94       */
95      ArduinoPanelSignal(final JFrame pFrame) {
96          /* Create scroll button */
97          theSelect = new ArduinoScrollButton<>(pFrame);
98          theSelect.setFormatter(ArduinoMessage::getName);
99          theSelect.onSelect(this::selectMessage);
100 
101         /* Create Panel for messageId */
102         theMsgId = new JLabel();
103         theMsgId.setFont(FONTX);
104         final JLabel myIdPrompt = new JLabel("MessageId:");
105         myIdPrompt.setFont(FONTX);
106         final JPanel myIdPanel = new JPanel();
107         myIdPanel.setLayout(new BoxLayout(myIdPanel, BoxLayout.X_AXIS));
108         myIdPanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
109         myIdPanel.add(myIdPrompt);
110         myIdPanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
111         myIdPanel.add(theMsgId);
112         myIdPanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
113 
114         /* Create Panel for selection */
115         final JPanel mySelectPanel = new JPanel();
116         mySelectPanel.setLayout(new BoxLayout(mySelectPanel, BoxLayout.X_AXIS));
117         mySelectPanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
118         mySelectPanel.add(theSelect.getComponent());
119         mySelectPanel.add(Box.createHorizontalGlue());
120         mySelectPanel.add(myIdPanel);
121         Border myBorder = BorderFactory.createTitledBorder("Select Message");
122         mySelectPanel.setBorder(myBorder);
123 
124         /* Create the table */
125         theTable = new ArduinoTableSignal(this);
126 
127         /* Create Panel for Data */
128         final JPanel myDataPanel = new JPanel(new GridBagLayout());
129         final GridBagConstraints myC = new GridBagConstraints();
130         myBorder = BorderFactory.createTitledBorder("Message Data");
131         myDataPanel.setBorder(myBorder);
132 
133         /* Add hexData */
134         myC.gridx = 0;
135         myC.gridy = 0;
136         myC.anchor = GridBagConstraints.LINE_END;
137         myC.weightx = 0.0;
138         myC.fill = GridBagConstraints.NONE;
139         myDataPanel.add(new JLabel("Hex Data: "), myC);
140         myC.gridx = 1;
141         myC.gridy = 0;
142         myC.anchor = GridBagConstraints.CENTER;
143         myC.weightx = 1.0;
144         myC.fill = GridBagConstraints.HORIZONTAL;
145         theHex = new JLabel();
146         theHex.setFont(FONT);
147         myDataPanel.add(theHex, myC);
148 
149         /* Add binaryData */
150         myC.gridx = 0;
151         myC.gridy = 1;
152         myC.anchor = GridBagConstraints.LINE_END;
153         myC.weightx = 0.0;
154         myC.fill = GridBagConstraints.NONE;
155         myDataPanel.add(new JLabel("Binary Data: "), myC);
156         myC.gridx = 1;
157         myC.gridy = 1;
158         myC.anchor = GridBagConstraints.CENTER;
159         myC.weightx = 1.0;
160         myC.fill = GridBagConstraints.HORIZONTAL;
161         theBinary = new BinaryData();
162         myDataPanel.add(theBinary.getComponent(), myC);
163 
164 
165         /* Create the message panel */
166         final JPanel myMsgPanel = new JPanel(new BorderLayout());
167         myMsgPanel.add(new JScrollPane(theTable.getComponent()), BorderLayout.CENTER);
168         myBorder = BorderFactory.createTitledBorder("Message Signals");
169         myMsgPanel.setBorder(myBorder);
170 
171         /* Create the panel */
172         thePanel = new JPanel(new BorderLayout());
173         thePanel.add(mySelectPanel, BorderLayout.PAGE_START);
174         thePanel.add(myMsgPanel, BorderLayout.CENTER);
175         thePanel.add(myDataPanel, BorderLayout.PAGE_END);
176 
177         /* Set Dimensions */
178         thePanel.setPreferredSize(new Dimension(ArduinoPanelMain.WIDTH, ArduinoPanelMain.HEIGHT));
179     }
180 
181     /**
182      * Obtain the component.
183      * @return the component
184      */
185     JComponent getComponent() {
186         return thePanel;
187     }
188 
189     /**
190      * Obtain the data.
191      * @return the data
192      */
193     byte[] getData() {
194         return theData;
195     }
196 
197     /**
198      * Set the system.
199      * @param pSystem the system
200      */
201     void setSystem(final ArduinoSystem pSystem) {
202         /* Build the new message selection list */
203         theSelect.removeAll();
204         for (ArduinoMessage myMessage : pSystem.getMessages()) {
205             theSelect.add(myMessage);
206         }
207     }
208 
209     /**
210      * Select the message.
211      * @param pMessage the message
212      */
213     private void selectMessage(final ArduinoMessage pMessage) {
214         /* Allocate the data */
215         theData = new byte[pMessage.getLength()];
216 
217         /* Configure the table */
218         theTable.configureTable(pMessage);
219 
220         /* Update the hex and binary */
221         updateHexAndBinary();
222         updateBitColors();
223         updateMessageId(pMessage);
224     }
225 
226     /**
227      * Update messageId.
228      * @param pMessage the message
229      */
230     void updateMessageId(final ArduinoMessage pMessage) {
231         final int myNum = Integer.parseUnsignedInt(pMessage.getId());
232         theMsgId.setText(pMessage.getId() + String.format("  0x%08X", myNum));
233     }
234 
235     /**
236      * Update hex and binary.
237      */
238     void updateHexAndBinary() {
239         updateHexData();
240         theBinary.refreshData();
241     }
242 
243     /**
244      * Update Hex Data.
245      */
246     private void updateHexData() {
247         theHex.setText(ArduinoHex.bytesToHexString(theData));
248     }
249 
250     /**
251      * Update BitColors.
252      */
253     void updateBitColors() {
254         /* Reset the colours */
255         theBinary.resetColors();
256 
257         /* Loop through the rows */
258         final int myNumRows = theTable.getNumRows();
259         for (int i = 0; i < myNumRows; i++) {
260             /* Obtain the colour for the signal */
261             final Color myColor = theTable.getColorForRow(i);
262 
263             /* If the signal is active */
264             if (myColor != null) {
265                 /* Access the bitMap for the row */
266                 final byte[] myBitMap = theTable.getBitMapForRow(i);
267 
268                 /* Update the colours */
269                 theBinary.updateColor(myColor, myBitMap);
270             }
271         }
272     }
273 
274     /**
275      * The binaryData panel.
276      */
277     private class BinaryData {
278         /**
279          * The Panel.
280          */
281         private final JPanel thePanel;
282 
283         /**
284          * The Binary Bit Panels.
285          */
286         private final BinaryBit[] theBits;
287 
288         /**
289          * Constructor.
290          */
291         BinaryData() {
292             /* Create the panel */
293             thePanel = new JPanel();
294             thePanel.setLayout(new BoxLayout(thePanel, BoxLayout.X_AXIS));
295 
296             /* Create the bit label array */
297             theBits = new BinaryBit[Long.SIZE];
298 
299             /* Create the bit panels */
300             for (int i = 0; i < Long.SIZE; i++) {
301                 /* Create the binary bit */
302                 final BinaryBit myBit = new BinaryBit(i);
303                 thePanel.add(myBit.getComponent());
304                 theBits[i] = myBit;
305 
306                 /* If we are on a byte boundary */
307                 if ((i + 1) % Byte.SIZE == 0) {
308                     final JLabel mySpacer = new JLabel();
309                     mySpacer.setText(" ");
310                     thePanel.add(mySpacer);
311                 }
312             }
313         }
314 
315         /**
316          * Obtain the component.
317          * @return the component
318          */
319         JComponent getComponent() {
320             return thePanel;
321         }
322 
323         /**
324          * Refresh the data.
325          */
326         void refreshData() {
327             /* determine the maxBit */
328             final int maxBit = theData.length * Byte.SIZE;
329 
330             /* Loop through the bits */
331             for (int i = 0; i < Long.SIZE; i++) {
332                 final boolean visible = i < maxBit;
333                 final BinaryBit myBit = theBits[i];
334                 myBit.refreshData(visible);
335             }
336         }
337 
338         /**
339          * Reset colors.
340          */
341         void resetColors() {
342             /* determine the maxBit */
343             final int maxBit = theData.length * Byte.SIZE;
344 
345             /* Loop through the bytes */
346             for (int bitNo = 0; bitNo < maxBit; bitNo++) {
347                 /* Set the colour */
348                 theBits[bitNo].setColor(thePanel.getBackground());
349             }
350         }
351 
352         /**
353          * Update color.
354          * @param pColour the colour
355          * @param pBitMap the bitMap
356          */
357         void updateColor(final Color pColour,
358                          final byte[] pBitMap) {
359             /* determine the maxBit */
360             final int maxBit = theData.length * Byte.SIZE;
361 
362             /* Loop through the bytes */
363             for (int bitNo = 0; bitNo < maxBit; bitNo++) {
364                 /* Access details */
365                 final int myByteNo = bitNo / Byte.SIZE;
366                 final int myShift = Byte.SIZE - (bitNo % Byte.SIZE) - 1;
367                 final int myMask = 1 << myShift;
368 
369                 /* If the bit is set */
370                 if ((pBitMap[myByteNo] & myMask) != 0) {
371                     /* Set the colour */
372                     theBits[bitNo].setColor(pColour);
373                 }
374             }
375         }
376     }
377 
378     /**
379      * The binaryBit panel.
380      */
381     private class BinaryBit {
382         /**
383          * The index.
384          */
385         private final int theIndex;
386 
387         /**
388          * The Panel.
389          */
390         private final JLabel theLabel;
391 
392         /**
393          * Constructor.
394          * @param pIndex the index
395          */
396         BinaryBit(final int pIndex) {
397             /* Store parameter */
398             theIndex = pIndex;
399 
400             /* Create the label */
401             theLabel = new JLabel();
402             theLabel.setFont(FONT);
403             theLabel.setHorizontalAlignment(SwingConstants.CENTER);
404             theLabel.setOpaque(true);
405 
406             /* Add the mouse adapter */
407             theLabel.addMouseListener(new LabelMouse());
408         }
409 
410         /**
411          * Obtain the component.
412          * @return the component
413          */
414         JComponent getComponent() {
415             return theLabel;
416         }
417 
418         /**
419          * Set color.
420          * @param pColour the colour
421          */
422         void setColor(final Color pColour) {
423             theLabel.setForeground(Color.BLACK);
424             theLabel.setBackground(pColour);
425         }
426 
427         /**
428          * Refresh the data.
429          * @param pVisible is the bit visible?
430          */
431         void refreshData(final boolean pVisible) {
432             theLabel.setVisible(pVisible);
433             if (pVisible) {
434                 showBitFromData();
435             }
436         }
437 
438         /**
439          * Display the bit value.
440          */
441         void showBitFromData() {
442             final int myByteNo = theIndex / Byte.SIZE;
443             final int myShift = Byte.SIZE - (theIndex % Byte.SIZE) - 1;
444             final int myMask = 1 << myShift;
445             theLabel.setText((theData[myByteNo] & myMask) != 0 ? "1" : "0");
446         }
447 
448         /**
449          * Toggle bit value.
450          */
451         void toggleBitInData() {
452             /* Toggle the bit */
453             final int myByteNo = theIndex / Byte.SIZE;
454             final int myShift = Byte.SIZE - (theIndex % Byte.SIZE) - 1;
455             final int myMask = 1 << myShift;
456             theData[myByteNo] ^= myMask;
457             theLabel.setText((theData[myByteNo] & myMask) != 0 ? "1" : "0");
458             refreshOnToggle();
459         }
460 
461         /**
462          * Refresh On Toggle.
463          */
464         private void refreshOnToggle() {
465             theTable.loadValues();
466             updateHexData();
467             updateBitColors();
468         }
469 
470         /**
471          * LabelMouse.
472          */
473         private final class LabelMouse
474                 extends MouseAdapter {
475             @Override
476             public void mouseClicked(final MouseEvent e) {
477                 toggleBitInData();
478             }
479         }
480     }
481 }