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.CardLayout;
21  import java.awt.Dimension;
22  import java.util.EnumMap;
23  import java.util.Map;
24  import java.util.Objects;
25  import javax.swing.BorderFactory;
26  import javax.swing.Box;
27  import javax.swing.BoxLayout;
28  import javax.swing.JButton;
29  import javax.swing.JComponent;
30  import javax.swing.JFrame;
31  import javax.swing.JLabel;
32  import javax.swing.JPanel;
33  import javax.swing.JScrollPane;
34  import javax.swing.JSeparator;
35  import javax.swing.border.Border;
36  
37  import net.sourceforge.jarduino.message.ArduinoAttribute.ArduinoAttrObject;
38  import net.sourceforge.jarduino.message.ArduinoChar;
39  import net.sourceforge.jarduino.message.ArduinoMessage;
40  import net.sourceforge.jarduino.message.ArduinoNamedObject;
41  import net.sourceforge.jarduino.message.ArduinoNode;
42  import net.sourceforge.jarduino.message.ArduinoSignal;
43  import net.sourceforge.jarduino.message.ArduinoSystem;
44  import net.sourceforge.jarduino.message.ArduinoValues;
45  
46  /**
47   * Configuration display.
48   */
49  public class ArduinoPanelMeta {
50      /**
51       * Attributes Text.
52       */
53      private static final String VIEW_ATTR = "Attributes";
54  
55      /**
56       * Values Text.
57       */
58      private static final String VIEW_VALUES = "Values";
59  
60      /**
61       * Connections Text.
62       */
63      private static final String VIEW_CONNECTS = "Connections";
64  
65      /**
66       * The panel.
67       */
68      private final JPanel thePanel;
69  
70      /**
71       * The Node Select button.
72       */
73      private final ArduinoScrollButton<ArduinoNode> theNode;
74  
75      /**
76       * The Message Select button.
77       */
78      private final ArduinoScrollButton<ArduinoMessage> theMessage;
79  
80      /**
81       * The Signal Select button.
82       */
83      private final ArduinoScrollButton<ArduinoSignal> theSignal;
84  
85      /**
86       * The View button.
87       */
88      private final JButton theViewButton;
89  
90      /**
91       * The button Panel map.
92       */
93      private final Map<ArduinoConfigMode, JPanel> theButtons;
94  
95      /**
96       * The Select Panel.
97       */
98      private final JPanel theSelect;
99  
100     /**
101      * The Filler Panel.
102      */
103     private final JPanel theFiller;
104 
105     /**
106      * The View Panel.
107      */
108     private final JPanel theView;
109 
110     /**
111      * The Comment label.
112      */
113     private final JLabel theComment;
114 
115     /**
116      * The Attributes table.
117      */
118     private final ArduinoTableAttr theAttrs;
119 
120     /**
121      * The Values table.
122      */
123     private final ArduinoTableValues theValues;
124 
125     /**
126      * The NodeConnect table.
127      */
128     private final ArduinoTableNodeConnect theNodeConnect;
129 
130     /**
131      * The MsgConnect table.
132      */
133     private final ArduinoTableMsgConnect theMsgConnect;
134 
135     /**
136      * The TablePanel.
137      */
138     private final JPanel theTablePanel;
139 
140     /**
141      * The mode.
142      */
143     private ArduinoConfigMode theMode;
144 
145     /**
146      * The system.
147      */
148     private ArduinoSystem theSystem;
149 
150     /**
151      * The selected object.
152      */
153     private ArduinoNamedObject theSelected;
154 
155     /**
156      * Do we show the valuesTable?
157      */
158     private boolean showValues;
159 
160     /**
161      * Constructor.
162      * @param pFrame the frame
163      */
164     ArduinoPanelMeta(final JFrame pFrame) {
165         /* Create the Select buttons */
166         theNode = new ArduinoScrollButton<>(pFrame);
167         theNode.setFormatter(ArduinoNode::getName);
168         theNode.onSelect(this::setSelected);
169         theMessage = new ArduinoScrollButton<>(pFrame);
170         theMessage.setFormatter(ArduinoMessage::getName);
171         theMessage.onSelect(this::setSelected);
172         theSignal = new ArduinoScrollButton<>(pFrame);
173         theSignal.setFormatter(ArduinoSignal::getName);
174         theSignal.onSelect(this::setSelected);
175 
176         /* Create the mode button */
177         final ArduinoScrollButton<ArduinoConfigMode> myModes = new ArduinoScrollButton<>(pFrame);
178         for (ArduinoConfigMode myMode : ArduinoConfigMode.values()) {
179             myModes.add(myMode);
180         }
181         myModes.setSelectedItem(ArduinoConfigMode.SYSTEM);
182         myModes.onSelect(this::selectMode);
183 
184         /* Create the button map */
185         theButtons = new EnumMap<>(ArduinoConfigMode.class);
186 
187         /* Create the mode panel */
188         final JPanel myModePanel = new JPanel();
189         myModePanel.setLayout(new BoxLayout(myModePanel, BoxLayout.X_AXIS));
190         myModePanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
191         myModePanel.add(myModes.getComponent());
192         myModePanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
193         Border myBorder = BorderFactory.createTitledBorder("Mode");
194         myModePanel.setBorder(myBorder);
195 
196         /* Create the item panel */
197         theSelect = new JPanel();
198         theSelect.setLayout(new BoxLayout(theSelect, BoxLayout.X_AXIS));
199         theSelect.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
200         theSelect.add(buildMsgPanel(ArduinoConfigMode.NODE, theNode));
201         theSelect.add(buildMsgPanel(ArduinoConfigMode.MESSAGE, theMessage));
202         theSelect.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
203         theSelect.add(buildMsgPanel(ArduinoConfigMode.SIGNAL, theSignal));
204         theSelect.add(Box.createHorizontalGlue());
205         myBorder = BorderFactory.createTitledBorder("Selection");
206         theSelect.setBorder(myBorder);
207 
208         /* Create the view button */
209         theViewButton = new JButton(VIEW_ATTR);
210         theViewButton.addActionListener(e -> {
211             showValues = !showValues;
212             theViewButton.setText(getViewButtonText());
213             updateView();
214         });
215 
216         /* Create the view panel */
217         theView = new JPanel();
218         theView.setLayout(new BoxLayout(theView, BoxLayout.X_AXIS));
219         theView.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
220         theView.add(theViewButton);
221         theView.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
222         myBorder = BorderFactory.createTitledBorder("View");
223         theView.setBorder(myBorder);
224 
225         /* Create the filler panel */
226         theFiller = new JPanel();
227         theFiller.setLayout(new BoxLayout(theFiller, BoxLayout.X_AXIS));
228         theFiller.add(Box.createHorizontalGlue());
229 
230         /* Create the selection panel */
231         final JPanel mySelectPanel = new JPanel();
232         mySelectPanel.setLayout(new BoxLayout(mySelectPanel, BoxLayout.X_AXIS));
233         mySelectPanel.add(myModePanel);
234         mySelectPanel.add(theSelect);
235         mySelectPanel.add(theFiller);
236         mySelectPanel.add(theView);
237 
238         /* Create the comment field */
239         theComment = new JLabel();
240         myBorder = BorderFactory.createTitledBorder("Comment");
241         theComment.setBorder(myBorder);
242         final JPanel myComment = new JPanel(new BorderLayout());
243         myComment.add(theComment);
244 
245         /* Create the header panel */
246         final JPanel myHeader = new JPanel();
247         myHeader.setLayout(new BoxLayout(myHeader, BoxLayout.Y_AXIS));
248         myHeader.add(mySelectPanel);
249         myHeader.add(new JSeparator());
250         myHeader.add(myComment);
251 
252         /* Create the attributes table */
253         theAttrs = new ArduinoTableAttr();
254         final JScrollPane myAttrScroll = new JScrollPane(theAttrs.getComponent());
255         myBorder = BorderFactory.createTitledBorder(VIEW_ATTR);
256         myAttrScroll.setBorder(myBorder);
257 
258         /* Create the values table */
259         theValues = new ArduinoTableValues();
260         final JScrollPane myValueScroll = new JScrollPane(theValues.getComponent());
261         myBorder = BorderFactory.createTitledBorder(VIEW_VALUES);
262         myValueScroll.setBorder(myBorder);
263 
264         /* Create the nodeConnect table */
265         theNodeConnect = new ArduinoTableNodeConnect();
266         myBorder = BorderFactory.createTitledBorder(VIEW_CONNECTS);
267         theNodeConnect.getComponent().setBorder(myBorder);
268 
269         /* Create the values table */
270         theMsgConnect = new ArduinoTableMsgConnect();
271         myBorder = BorderFactory.createTitledBorder(VIEW_CONNECTS);
272         theMsgConnect.getComponent().setBorder(myBorder);
273 
274         /* Create the table card */
275         theTablePanel = new JPanel(new CardLayout());
276         theTablePanel.add(myAttrScroll, VIEW_ATTR);
277         theTablePanel.add(myValueScroll, VIEW_VALUES);
278         theTablePanel.add(theNodeConnect.getComponent(), VIEW_CONNECTS + ArduinoConfigMode.NODE.toString());
279         theTablePanel.add(theMsgConnect.getComponent(), VIEW_CONNECTS + ArduinoConfigMode.MESSAGE.toString());
280 
281         /* Create the panel */
282         thePanel = new JPanel(new BorderLayout());
283         thePanel.add(myHeader, BorderLayout.PAGE_START);
284         thePanel.add(theTablePanel, BorderLayout.CENTER);
285 
286         /* Default to system */
287         theMode = ArduinoConfigMode.SYSTEM;
288     }
289 
290     /**
291      * Obtain the component.
292      * @return the component
293      */
294     JComponent getComponent() {
295         return thePanel;
296     }
297 
298     /**
299      * Select Modes.
300      * @param pMode the mode
301      */
302     private void selectMode(final ArduinoConfigMode pMode) {
303         /* If this is the existing mode, just return */
304         if (theMode == pMode) {
305             return;
306         }
307 
308         /* Set visibility */
309         setVisibility(pMode);
310 
311         /* Switch on mode */
312         theMode = pMode;
313         switch (theMode) {
314             case NODE:
315                 setSelected(theNode.getSelectedItem());
316                 break;
317             case MESSAGE:
318                 setSelected(theMessage.getSelectedItem());
319                 break;
320             case SIGNAL:
321                 /* Careful if signal is selected before any message */
322                 final ArduinoSignal mySignal = theSignal.getSelectedItem();
323                 setSelected(mySignal == null ? theMessage.getSelectedItem() : mySignal);
324                 break;
325             case SYSTEM:
326             default:
327                 setSelected(theSystem);
328                 break;
329         }
330 
331         /* Update the view button */
332         theViewButton.setText(getViewButtonText());
333     }
334 
335     /**
336      * Set selected Object.
337      * @param pObject the selected object
338      */
339     private void setSelected(final ArduinoNamedObject pObject) {
340         /* Ignore if the object is irrelevant */
341         if (!theMode.checkObject(pObject)
342              || Objects.equals(theSelected, pObject)) {
343             return;
344         }
345 
346         /* If we have a message when we are in signal Mode */
347         if (pObject instanceof ArduinoMessage) {
348             /* Ignore if the object is the owner of the selected */
349             final ArduinoMessage/net/sourceforge/jarduino/message/ArduinoMessage.html#ArduinoMessage">ArduinoMessage myMessage = (ArduinoMessage) pObject;
350             if (theMode == ArduinoConfigMode.SIGNAL
351                     && theSelected instanceof ArduinoSignal
352                     && pObject.equals(((ArduinoSignal) theSelected).getOwner())) {
353                 return;
354             }
355 
356             /* Populate the signal list */
357             theSignal.removeAll();
358             for (ArduinoSignal mySignal : myMessage.getAllSignals()) {
359                 /* Add to the list */
360                 theSignal.add(mySignal);
361             }
362 
363             /* Return if we are in signal mode */
364             if (theMode == ArduinoConfigMode.SIGNAL) {
365                 return;
366             }
367         }
368 
369         /* Set the details for the object */
370         setComment(pObject);
371         theAttrs.configureTable(theSystem, theMode, (ArduinoAttrObject) pObject);
372         theValues.getComponent().setVisible(false);
373         theView.setVisible(false);
374         if (pObject instanceof ArduinoSignal) {
375             final ArduinoValues myValues = ((ArduinoSignal) pObject).getValues();
376             if (myValues != null) {
377                 theValues.configureTable(myValues);
378                 theValues.getComponent().setVisible(true);
379                 theView.setVisible(true);
380             }
381         }
382         if (pObject instanceof ArduinoNode) {
383             theNodeConnect.configureTable((ArduinoNode) pObject);
384             theView.setVisible(true);
385         }
386         if (pObject instanceof ArduinoMessage) {
387             theMsgConnect.configureTable((ArduinoMessage) pObject);
388             theView.setVisible(true);
389         }
390         theSelected = pObject;
391         updateView();
392 
393         /* Set Dimensions */
394         thePanel.setPreferredSize(new Dimension(ArduinoPanelMain.WIDTH, ArduinoPanelMain.HEIGHT));
395     }
396 
397     /**
398      * Set comment.
399      * @param pObject the selected object
400      */
401     private void setComment(final ArduinoNamedObject pObject) {
402         /* Access the comment for the object */
403         String myComment = theSystem.getCommentForObject(pObject);
404 
405         /* Clean the comment */
406         myComment = cleanseLabelText(myComment);
407 
408         /* Set text and visibility */
409         theComment.setText(myComment);
410         theComment.setVisible(myComment != null && !showValues);
411     }
412 
413     /**
414      * Clean label text to handle embedded new line.
415      * @param pText the text
416      * @return the cleansed text
417      */
418     static String cleanseLabelText(final String pText) {
419         /* Access the text */
420         String myText = pText;
421 
422         /* If the text includes a linefeed */
423         if (myText != null
424                 && myText.indexOf(ArduinoChar.LF) != -1) {
425             /* Convert to html */
426             myText = "<html>"
427                     + myText.replace("&", "&amp;")
428                     .replace(">", "&gt;")
429                     .replace("<", "&lt;")
430                     .replace(Character.toString(ArduinoChar.LF), "<br>");
431         }
432 
433         /* Return the text */
434         return myText;
435     }
436 
437     /**
438      * Create a labelled button panel.
439      * @param pMode the mode
440      * @param pButton the button
441      * @return the panel
442      */
443     private JPanel buildMsgPanel(final ArduinoConfigMode pMode,
444                                  final ArduinoScrollButton<?> pButton) {
445         /* Create the panel */
446         final JPanel myPanel = new JPanel();
447         myPanel.setLayout(new BoxLayout(myPanel, BoxLayout.X_AXIS));
448         myPanel.add(new JLabel(pMode.toString() + ArduinoChar.COLON));
449         myPanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
450         myPanel.add(pButton.getComponent());
451 
452         /* Store into map and return */
453         theButtons.put(pMode, myPanel);
454         return myPanel;
455     }
456 
457     /**
458      * Set visibility.
459      * @param pMode the mode
460      */
461     private void setVisibility(final ArduinoConfigMode pMode) {
462         /* Determine visibility */
463         final boolean bNodeVisible = pMode == ArduinoConfigMode.NODE;
464         final boolean bSignalVisible = pMode == ArduinoConfigMode.SIGNAL;
465         final boolean bMessageVisible = pMode == ArduinoConfigMode.MESSAGE || bSignalVisible;
466         final boolean bSelectVisible = pMode != ArduinoConfigMode.SYSTEM;
467 
468         /* Update the button panels */
469         theButtons.get(ArduinoConfigMode.NODE).setVisible(bNodeVisible);
470         theButtons.get(ArduinoConfigMode.MESSAGE).setVisible(bMessageVisible);
471         theButtons.get(ArduinoConfigMode.SIGNAL).setVisible(bSignalVisible);
472 
473         /* Update selection/filler */
474         theSelect.setVisible(bSelectVisible);
475         theFiller.setVisible(!bSelectVisible);
476     }
477 
478     /**
479      * Update the view.
480      */
481     private void updateView() {
482         /* Determine the card to show */
483         String myCard = VIEW_ATTR;
484         if (showValues) {
485             if (theSelected instanceof ArduinoNode) {
486                 myCard = VIEW_CONNECTS + ArduinoConfigMode.NODE.toString();
487             } else if (theSelected instanceof ArduinoMessage) {
488                 myCard = VIEW_CONNECTS + ArduinoConfigMode.MESSAGE.toString();
489             } else if (theSelected instanceof ArduinoSignal
490                     && ((ArduinoSignal) theSelected).getValues() != null) {
491                 myCard = VIEW_VALUES;
492             }
493         }
494 
495         /* Determine if we are showing connections */
496         final CardLayout myLayout = (CardLayout) theTablePanel.getLayout();
497         myLayout.show(theTablePanel, myCard);
498     }
499 
500     /**
501      * Determine the viewButton Text.
502      * @return the text
503      */
504     private String getViewButtonText() {
505         /* Determine the card to show */
506         if (showValues) {
507             switch (theMode) {
508                 case NODE:
509                 case MESSAGE:
510                     return VIEW_CONNECTS;
511                 case SIGNAL:
512                     return VIEW_VALUES;
513                 default:
514                     break;
515             }
516         }
517         return VIEW_ATTR;
518     }
519 
520     /**
521      * Set the system.
522      * @param pSystem the system
523      */
524     void setSystem(final ArduinoSystem pSystem) {
525         /* Store system */
526         theSystem = pSystem;
527 
528         /* clear the selection lists */
529         theSelected = null;
530         theNode.removeAll();
531         theMessage.removeAll();
532         theSignal.removeAll();
533 
534         /* Loop through the nodes */
535         for (ArduinoNode myNode : theSystem.getNodes()) {
536             /* Add to the list (unless it is the nullNode) */
537             if (!myNode.getName().equals(ArduinoNode.NULL_NODE)) {
538                 theNode.add(myNode);
539             }
540 
541             /* Loop through the messages */
542             for (ArduinoMessage myMessage : myNode.getMessages()) {
543                 /* Add to the list */
544                 theMessage.add(myMessage);
545             }
546         }
547 
548         /* Reset the mode */
549         final ArduinoConfigMode myMode = theMode;
550         theMode = null;
551         selectMode(myMode);
552     }
553 
554     /**
555      * The modes.
556      */
557     enum ArduinoConfigMode {
558         /**
559          * System.
560          */
561         SYSTEM("System"),
562 
563         /**
564          * Node.
565          */
566         NODE("Node"),
567 
568         /**
569          * Message.
570          */
571         MESSAGE("Message"),
572 
573         /**
574          * Signal.
575          */
576         SIGNAL("Signal");
577 
578         /**
579          * The name.
580          */
581         private final String theName;
582 
583         /**
584          * Constructor.
585          * @param pName the Name
586          */
587         ArduinoConfigMode(final String pName) {
588             theName = pName;
589         }
590 
591         /**
592          * Check that object is of the correct type.
593          * @param pObject the object
594          * @return true/false
595          */
596         boolean checkObject(final ArduinoNamedObject pObject) {
597             switch (this) {
598                 case NODE:
599                     return pObject instanceof ArduinoNode;
600                 case MESSAGE:
601                     return pObject instanceof ArduinoMessage;
602                 case SIGNAL:
603                     return pObject instanceof ArduinoMessage
604                             || pObject instanceof ArduinoSignal;
605                 case SYSTEM:
606                     return pObject instanceof ArduinoSystem;
607                 default:
608                     return false;
609             }
610         }
611 
612         @Override
613         public String toString() {
614             return theName;
615         }
616     }
617 }